1 use rustc_errors::Applicability;
3 intravisit::{walk_expr, NestedVisitorMap, Visitor},
6 use rustc_lint::LateContext;
7 use rustc_middle::{hir::map::Map, ty, ty::Ty};
8 use rustc_span::source_map::Span;
10 use crate::utils::{match_trait_method, match_type, paths, snippet, span_lint_and_then};
12 use if_chain::if_chain;
14 pub(super) fn lint(cx: &LateContext<'_>, expr: &'tcx Expr<'_>, args: &[&[Expr<'_>]]) {
19 let for_each_args = args[0];
20 if for_each_args.len() < 2 {
23 let for_each_receiver = &for_each_args[0];
24 let for_each_arg = &for_each_args[1];
25 let iter_receiver = &args[1][0];
28 if match_trait_method(cx, expr, &paths::ITERATOR);
29 if !match_trait_method(cx, for_each_receiver, &paths::ITERATOR);
30 if is_target_ty(cx, cx.typeck_results().expr_ty(iter_receiver));
31 if let ExprKind::Closure(_, _, body_id, ..) = for_each_arg.kind;
33 let body = cx.tcx.hir().body(body_id);
34 let mut ret_span_collector = RetSpanCollector::new();
35 ret_span_collector.visit_expr(&body.value);
38 let loop_label = if ret_span_collector.need_label {
39 format!("{}: ", label)
44 format!("{}for {} in {} {{ .. }}", loop_label, snippet(cx, body.params[0].pat.span, ""), snippet(cx, for_each_receiver.span, ""));
46 let mut notes = vec![];
47 for (span, need_label) in ret_span_collector.spans {
48 let cont_label = if need_label {
53 let note = format!("change `return` to `continue{}` in the loop body", cont_label);
54 notes.push((span, note));
57 span_lint_and_then(cx,
58 super::EXCESSIVE_FOR_EACH,
60 "excessive use of `for_each`",
62 diag.span_suggestion(expr.span, "try", sugg, Applicability::HasPlaceholders);
64 diag.span_note(note.0, ¬e.1);
72 type PathSegment = &'static [&'static str];
74 const TARGET_ITER_RECEIVER_TY: &[PathSegment] = &[
85 fn is_target_ty(cx: &LateContext<'_>, expr_ty: Ty<'_>) -> bool {
86 let expr_ty = expr_ty.peel_refs();
87 for target in TARGET_ITER_RECEIVER_TY {
88 if match_type(cx, expr_ty, target) {
94 if matches!(expr_ty.kind(), ty::Slice(_) | ty::Array(..));
103 /// Collect spans of `return` in the closure body.
104 struct RetSpanCollector {
105 spans: Vec<(Span, bool)>,
110 impl RetSpanCollector {
120 impl<'tcx> Visitor<'tcx> for RetSpanCollector {
121 type Map = Map<'tcx>;
123 fn visit_expr(&mut self, expr: &Expr<'_>) {
125 ExprKind::Ret(..) => {
126 if self.loop_depth > 0 && !self.need_label {
127 self.need_label = true
130 self.spans.push((expr.span, self.loop_depth > 0))
133 ExprKind::Loop(..) => {
134 self.loop_depth += 1;
135 walk_expr(self, expr);
136 self.loop_depth -= 1;
143 walk_expr(self, expr);
146 fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
147 NestedVisitorMap::None