@@ -10,9 +10,7 @@ use rustc_span::{source_map::Span, sym, Symbol};
1010
1111use if_chain:: if_chain;
1212
13- use crate :: utils:: {
14- has_iter_method, is_diagnostic_assoc_item, method_calls, snippet_with_applicability, span_lint_and_then,
15- } ;
13+ use crate :: utils:: { has_iter_method, is_trait_method, snippet_with_applicability, span_lint_and_then} ;
1614
1715declare_clippy_lint ! {
1816 /// **What it does:** Checks for usage of `for_each` that would be more simply written as a
@@ -41,7 +39,7 @@ declare_clippy_lint! {
4139 /// }
4240 /// ```
4341 pub NEEDLESS_FOR_EACH ,
44- restriction ,
42+ pedantic ,
4543 "using `for_each` where a `for` loop would be simpler"
4644}
4745
@@ -55,22 +53,28 @@ impl LateLintPass<'_> for NeedlessForEach {
5553 _ => return ,
5654 } ;
5755
58- // Max depth is set to 3 because we need to check the method chain length is just two.
59- let ( method_names, arg_lists, _) = method_calls ( expr, 3 ) ;
60-
6156 if_chain ! {
62- // This assures the length of this method chain is two.
63- if let [ for_each_args, iter_args] = arg_lists. as_slice( ) ;
64- if let Some ( for_each_sym) = method_names. first( ) ;
65- if * for_each_sym == Symbol :: intern( "for_each" ) ;
66- if let Some ( did) = cx. typeck_results( ) . type_dependent_def_id( expr. hir_id) ;
67- if is_diagnostic_assoc_item( cx, did, sym:: Iterator ) ;
68- // Checks the type of the first method receiver is NOT a user defined type.
69- if has_iter_method( cx, cx. typeck_results( ) . expr_ty( & iter_args[ 0 ] ) ) . is_some( ) ;
70- if let ExprKind :: Closure ( _, _, body_id, ..) = for_each_args[ 1 ] . kind;
71- let body = cx. tcx. hir( ) . body( body_id) ;
57+ // Check the method name is `for_each`.
58+ if let ExprKind :: MethodCall ( method_name, _, for_each_args, _) = expr. kind;
59+ if method_name. ident. name == Symbol :: intern( "for_each" ) ;
60+ // Check `for_each` is an associated function of `Iterator`.
61+ if is_trait_method( cx, expr, sym:: Iterator ) ;
62+ // Checks the receiver of `for_each` is also a method call.
63+ if let Some ( for_each_receiver) = for_each_args. get( 0 ) ;
64+ if let ExprKind :: MethodCall ( _, _, iter_args, _) = for_each_receiver. kind;
65+ // Skip the lint if the call chain is too long. e.g. `v.field.iter().for_each()` or
66+ // `v.foo().iter().for_each()` must be skipped.
67+ if let Some ( iter_receiver) = iter_args. get( 0 ) ;
68+ if matches!(
69+ iter_receiver. kind,
70+ ExprKind :: Array ( ..) | ExprKind :: Call ( ..) | ExprKind :: Path ( ..)
71+ ) ;
72+ // Checks the type of the `iter` method receiver is NOT a user defined type.
73+ if has_iter_method( cx, cx. typeck_results( ) . expr_ty( & iter_receiver) ) . is_some( ) ;
7274 // Skip the lint if the body is not block because this is simpler than `for` loop.
7375 // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop.
76+ if let ExprKind :: Closure ( _, _, body_id, ..) = for_each_args[ 1 ] . kind;
77+ let body = cx. tcx. hir( ) . body( body_id) ;
7478 if let ExprKind :: Block ( ..) = body. value. kind;
7579 then {
7680 let mut ret_collector = RetCollector :: default ( ) ;
@@ -99,7 +103,7 @@ impl LateLintPass<'_> for NeedlessForEach {
99103 ) ) ) ;
100104
101105 for span in & ret_collector. spans {
102- suggs. push( ( * span, "return " . to_string( ) ) ) ;
106+ suggs. push( ( * span, "continue " . to_string( ) ) ) ;
103107 }
104108
105109 span_lint_and_then(
0 commit comments