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 is_target_ty(cx, cx.typeck_results().expr_ty(iter_receiver));
30 if let ExprKind::Closure(_, _, body_id, ..) = for_each_arg.kind;
32 let body = cx.tcx.hir().body(body_id);
33 let mut ret_span_collector = RetSpanCollector::new();
34 ret_span_collector.visit_expr(&body.value);
37 let loop_label = if ret_span_collector.need_label {
38 format!("{}: ", label)
43 format!("{}for {} in {} {{ .. }}", loop_label, snippet(cx, body.params[0].pat.span, ""), snippet(cx, for_each_receiver.span, ""));
45 let mut notes = vec![];
46 for (span, need_label) in ret_span_collector.spans {
47 let cont_label = if need_label {
52 let note = format!("change `return` to `continue{}` in the loop body", cont_label);
53 notes.push((span, note));
56 span_lint_and_then(cx,
57 super::EXCESSIVE_FOR_EACH,
59 "excessive use of `for_each`",
61 diag.span_suggestion(expr.span, "try", sugg, Applicability::HasPlaceholders);
63 diag.span_note(note.0, ¬e.1);
71 type PathSegment = &'static [&'static str];
73 const TARGET_ITER_RECEIVER_TY: &[PathSegment] = &[
84 fn is_target_ty(cx: &LateContext<'_>, expr_ty: Ty<'_>) -> bool {
85 let expr_ty = expr_ty.peel_refs();
86 for target in TARGET_ITER_RECEIVER_TY {
87 if match_type(cx, expr_ty, target) {
93 if matches!(expr_ty.kind(), ty::Slice(_) | ty::Array(..));
102 /// Collect spans of `return` in the closure body.
103 struct RetSpanCollector {
104 spans: Vec<(Span, bool)>,
109 impl RetSpanCollector {
119 impl<'tcx> Visitor<'tcx> for RetSpanCollector {
120 type Map = Map<'tcx>;
122 fn visit_expr(&mut self, expr: &Expr<'_>) {
124 ExprKind::Ret(..) => {
125 if self.loop_depth > 0 && !self.need_label {
126 self.need_label = true
129 self.spans.push((expr.span, self.loop_depth > 0))
132 ExprKind::Loop(..) => {
133 self.loop_depth += 1;
134 walk_expr(self, expr);
135 self.loop_depth -= 1;
142 walk_expr(self, expr);
145 fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
146 NestedVisitorMap::None