1 use super::utils::{LoopNestVisitor, Nesting};
2 use super::WHILE_LET_ON_ITERATOR;
3 use crate::utils::usage::mutated_variables;
5 get_enclosing_block, is_refutable, is_trait_method, last_path_segment, path_to_local, path_to_local_id,
6 snippet_with_applicability, span_lint_and_sugg,
8 use clippy_utils::ty::implements_trait;
9 use if_chain::if_chain;
10 use rustc_errors::Applicability;
11 use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor};
12 use rustc_hir::{Expr, ExprKind, HirId, MatchSource, Node, PatKind};
13 use rustc_lint::LateContext;
14 use rustc_middle::hir::map::Map;
15 use rustc_span::symbol::sym;
17 pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
18 if let ExprKind::Match(ref match_expr, ref arms, MatchSource::WhileLetDesugar) = expr.kind {
19 let pat = &arms[0].pat.kind;
21 &PatKind::TupleStruct(ref qpath, ref pat_args, _),
22 &ExprKind::MethodCall(ref method_path, _, ref method_args, _),
23 ) = (pat, &match_expr.kind)
25 let iter_expr = &method_args[0];
27 // Don't lint when the iterator is recreated on every iteration
29 if let ExprKind::MethodCall(..) | ExprKind::Call(..) = iter_expr.kind;
30 if let Some(iter_def_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
31 if implements_trait(cx, cx.typeck_results().expr_ty(iter_expr), iter_def_id, &[]);
37 let lhs_constructor = last_path_segment(qpath);
38 if method_path.ident.name == sym::next
39 && is_trait_method(cx, match_expr, sym::Iterator)
40 && lhs_constructor.ident.name == sym::Some
41 && (pat_args.is_empty()
42 || !is_refutable(cx, &pat_args[0])
43 && !is_used_inside(cx, iter_expr, &arms[0].body)
44 && !is_iterator_used_after_while_let(cx, iter_expr)
45 && !is_nested(cx, expr, &method_args[0]))
47 let mut applicability = Applicability::MachineApplicable;
48 let iterator = snippet_with_applicability(cx, method_args[0].span, "_", &mut applicability);
49 let loop_var = if pat_args.is_empty() {
52 snippet_with_applicability(cx, pat_args[0].span, "_", &mut applicability).into_owned()
56 WHILE_LET_ON_ITERATOR,
57 expr.span.with_hi(match_expr.span.hi()),
58 "this loop could be written as a `for` loop",
60 format!("for {} in {}", loop_var, iterator),
68 fn is_used_inside<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, container: &'tcx Expr<'_>) -> bool {
69 let def_id = match path_to_local(expr) {
73 if let Some(used_mutably) = mutated_variables(container, cx) {
74 if used_mutably.contains(&def_id) {
81 fn is_iterator_used_after_while_let<'tcx>(cx: &LateContext<'tcx>, iter_expr: &'tcx Expr<'_>) -> bool {
82 let def_id = match path_to_local(iter_expr) {
86 let mut visitor = VarUsedAfterLoopVisitor {
88 iter_expr_id: iter_expr.hir_id,
89 past_while_let: false,
90 var_used_after_while_let: false,
92 if let Some(enclosing_block) = get_enclosing_block(cx, def_id) {
93 walk_block(&mut visitor, enclosing_block);
95 visitor.var_used_after_while_let
98 fn is_nested(cx: &LateContext<'_>, match_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool {
100 if let Some(loop_block) = get_enclosing_block(cx, match_expr.hir_id);
101 let parent_node = cx.tcx.hir().get_parent_node(loop_block.hir_id);
102 if let Some(Node::Expr(loop_expr)) = cx.tcx.hir().find(parent_node);
104 return is_loop_nested(cx, loop_expr, iter_expr)
110 fn is_loop_nested(cx: &LateContext<'_>, loop_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool {
111 let mut id = loop_expr.hir_id;
112 let iter_id = if let Some(id) = path_to_local(iter_expr) {
118 let parent = cx.tcx.hir().get_parent_node(id);
122 match cx.tcx.hir().find(parent) {
123 Some(Node::Expr(expr)) => {
124 if let ExprKind::Loop(..) = expr.kind {
128 Some(Node::Block(block)) => {
129 let mut block_visitor = LoopNestVisitor {
132 nesting: Nesting::Unknown,
134 walk_block(&mut block_visitor, block);
135 if block_visitor.nesting == Nesting::RuledOut {
139 Some(Node::Stmt(_)) => (),
148 struct VarUsedAfterLoopVisitor {
151 past_while_let: bool,
152 var_used_after_while_let: bool,
155 impl<'tcx> Visitor<'tcx> for VarUsedAfterLoopVisitor {
156 type Map = Map<'tcx>;
158 fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
159 if self.past_while_let {
160 if path_to_local_id(expr, self.def_id) {
161 self.var_used_after_while_let = true;
163 } else if self.iter_expr_id == expr.hir_id {
164 self.past_while_let = true;
166 walk_expr(self, expr);
168 fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
169 NestedVisitorMap::None