1 use rustc_errors
::Applicability
;
3 intravisit
::{walk_expr, Visitor}
,
4 Expr
, ExprKind
, Stmt
, StmtKind
,
6 use rustc_lint
::{LateContext, LateLintPass}
;
7 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
8 use rustc_span
::{source_map::Span, sym, Symbol}
;
10 use if_chain
::if_chain
;
12 use clippy_utils
::diagnostics
::span_lint_and_then
;
13 use clippy_utils
::is_trait_method
;
14 use clippy_utils
::source
::snippet_with_applicability
;
15 use clippy_utils
::ty
::has_iter_method
;
17 declare_clippy_lint
! {
19 /// Checks for usage of `for_each` that would be more simply written as a
22 /// ### Why is this bad?
23 /// `for_each` may be used after applying iterator transformers like
24 /// `filter` for better readability and performance. It may also be used to fit a simple
25 /// operation on one line.
26 /// But when none of these apply, a simple `for` loop is more idiomatic.
30 /// let v = vec![0, 1, 2];
31 /// v.iter().for_each(|elem| {
32 /// println!("{}", elem);
37 /// let v = vec![0, 1, 2];
38 /// for elem in v.iter() {
39 /// println!("{}", elem);
42 #[clippy::version = "1.53.0"]
43 pub NEEDLESS_FOR_EACH
,
45 "using `for_each` where a `for` loop would be simpler"
48 declare_lint_pass
!(NeedlessForEach
=> [NEEDLESS_FOR_EACH
]);
50 impl<'tcx
> LateLintPass
<'tcx
> for NeedlessForEach
{
51 fn check_stmt(&mut self, cx
: &LateContext
<'tcx
>, stmt
: &'tcx Stmt
<'_
>) {
52 let expr
= match stmt
.kind
{
53 StmtKind
::Expr(expr
) | StmtKind
::Semi(expr
) => expr
,
58 // Check the method name is `for_each`.
59 if let ExprKind
::MethodCall(method_name
, [for_each_recv
, for_each_arg
], _
) = expr
.kind
;
60 if method_name
.ident
.name
== Symbol
::intern("for_each");
61 // Check `for_each` is an associated function of `Iterator`.
62 if is_trait_method(cx
, expr
, sym
::Iterator
);
63 // Checks the receiver of `for_each` is also a method call.
64 if let ExprKind
::MethodCall(_
, [iter_recv
], _
) = for_each_recv
.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.
69 ExprKind
::Array(..) | ExprKind
::Call(..) | ExprKind
::Path(..)
71 // Checks the type of the `iter` method receiver is NOT a user defined type.
72 if has_iter_method(cx
, cx
.typeck_results().expr_ty(iter_recv
)).is_some();
73 // Skip the lint if the body is not block because this is simpler than `for` loop.
74 // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop.
75 if let ExprKind
::Closure { body, .. }
= for_each_arg
.kind
;
76 let body
= cx
.tcx
.hir().body(body
);
77 if let ExprKind
::Block(..) = body
.value
.kind
;
79 let mut ret_collector
= RetCollector
::default();
80 ret_collector
.visit_expr(&body
.value
);
82 // Skip the lint if `return` is used in `Loop` in order not to suggest using `'label`.
83 if ret_collector
.ret_in_loop
{
87 let (mut applicability
, ret_suggs
) = if ret_collector
.spans
.is_empty() {
88 (Applicability
::MachineApplicable
, None
)
91 Applicability
::MaybeIncorrect
,
96 .map(|span
| (span
, "continue".to_string()))
104 snippet_with_applicability(cx
, body
.params
[0].pat
.span
, "..", &mut applicability
),
105 snippet_with_applicability(cx
, for_each_recv
.span
, "..", &mut applicability
),
106 snippet_with_applicability(cx
, body
.value
.span
, "..", &mut applicability
),
109 span_lint_and_then(cx
, NEEDLESS_FOR_EACH
, stmt
.span
, "needless use of `for_each`", |diag
| {
110 diag
.span_suggestion(stmt
.span
, "try", sugg
, applicability
);
111 if let Some(ret_suggs
) = ret_suggs
{
112 diag
.multipart_suggestion("...and replace `return` with `continue`", ret_suggs
, applicability
);
120 /// This type plays two roles.
121 /// 1. Collect spans of `return` in the closure body.
122 /// 2. Detect use of `return` in `Loop` in the closure body.
124 /// NOTE: The functionality of this type is similar to
125 /// [`clippy_utils::visitors::find_all_ret_expressions`], but we can't use
126 /// `find_all_ret_expressions` instead of this type. The reasons are:
127 /// 1. `find_all_ret_expressions` passes the argument of `ExprKind::Ret` to a callback, but what we
128 /// need here is `ExprKind::Ret` itself.
129 /// 2. We can't trace current loop depth with `find_all_ret_expressions`.
131 struct RetCollector
{
137 impl<'tcx
> Visitor
<'tcx
> for RetCollector
{
138 fn visit_expr(&mut self, expr
: &Expr
<'_
>) {
140 ExprKind
::Ret(..) => {
141 if self.loop_depth
> 0 && !self.ret_in_loop
{
142 self.ret_in_loop
= true;
145 self.spans
.push(expr
.span
);
148 ExprKind
::Loop(..) => {
149 self.loop_depth
+= 1;
150 walk_expr(self, expr
);
151 self.loop_depth
-= 1;
158 walk_expr(self, expr
);