1 use super::utils::{LoopNestVisitor, Nesting};
2 use super::WHILE_LET_ON_ITERATOR;
3 use clippy_utils::diagnostics::span_lint_and_sugg;
4 use clippy_utils::source::snippet_with_applicability;
5 use clippy_utils::ty::implements_trait;
6 use clippy_utils::usage::mutated_variables;
8 get_enclosing_block, is_refutable, is_trait_method, last_path_segment, path_to_local, path_to_local_id,
10 use if_chain::if_chain;
11 use rustc_errors::Applicability;
12 use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor};
13 use rustc_hir::{Expr, ExprKind, HirId, MatchSource, Node, PatKind};
14 use rustc_lint::LateContext;
15 use rustc_middle::hir::map::Map;
16 use rustc_span::symbol::sym;
18 pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
19 if let ExprKind::Match(ref match_expr, ref arms, MatchSource::WhileLetDesugar) = expr.kind {
20 let pat = &arms[0].pat.kind;
22 &PatKind::TupleStruct(ref qpath, ref pat_args, _),
23 &ExprKind::MethodCall(ref method_path, _, ref method_args, _),
24 ) = (pat, &match_expr.kind)
26 let iter_expr = &method_args[0];
28 // Don't lint when the iterator is recreated on every iteration
30 if let ExprKind::MethodCall(..) | ExprKind::Call(..) = iter_expr.kind;
31 if let Some(iter_def_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
32 if implements_trait(cx, cx.typeck_results().expr_ty(iter_expr), iter_def_id, &[]);
38 let lhs_constructor = last_path_segment(qpath);
39 if method_path.ident.name == sym::next
40 && is_trait_method(cx, match_expr, sym::Iterator)
41 && lhs_constructor.ident.name == sym::Some
42 && (pat_args.is_empty()
43 || !is_refutable(cx, &pat_args[0])
44 && !is_used_inside(cx, iter_expr, &arms[0].body)
45 && !is_iterator_used_after_while_let(cx, iter_expr)
46 && !is_nested(cx, expr, &method_args[0]))
48 let mut applicability = Applicability::MachineApplicable;
49 let iterator = snippet_with_applicability(cx, method_args[0].span, "_", &mut applicability);
50 let loop_var = if pat_args.is_empty() {
53 snippet_with_applicability(cx, pat_args[0].span, "_", &mut applicability).into_owned()
57 WHILE_LET_ON_ITERATOR,
58 expr.span.with_hi(match_expr.span.hi()),
59 "this loop could be written as a `for` loop",
61 format!("for {} in {}", loop_var, iterator),
69 fn is_used_inside<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, container: &'tcx Expr<'_>) -> bool {
70 let def_id = match path_to_local(expr) {
74 if let Some(used_mutably) = mutated_variables(container, cx) {
75 if used_mutably.contains(&def_id) {
82 fn is_iterator_used_after_while_let<'tcx>(cx: &LateContext<'tcx>, iter_expr: &'tcx Expr<'_>) -> bool {
83 let def_id = match path_to_local(iter_expr) {
87 let mut visitor = VarUsedAfterLoopVisitor {
89 iter_expr_id: iter_expr.hir_id,
90 past_while_let: false,
91 var_used_after_while_let: false,
93 if let Some(enclosing_block) = get_enclosing_block(cx, def_id) {
94 walk_block(&mut visitor, enclosing_block);
96 visitor.var_used_after_while_let
99 fn is_nested(cx: &LateContext<'_>, match_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool {
101 if let Some(loop_block) = get_enclosing_block(cx, match_expr.hir_id);
102 let parent_node = cx.tcx.hir().get_parent_node(loop_block.hir_id);
103 if let Some(Node::Expr(loop_expr)) = cx.tcx.hir().find(parent_node);
105 return is_loop_nested(cx, loop_expr, iter_expr)
111 fn is_loop_nested(cx: &LateContext<'_>, loop_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool {
112 let mut id = loop_expr.hir_id;
113 let iter_id = if let Some(id) = path_to_local(iter_expr) {
119 let parent = cx.tcx.hir().get_parent_node(id);
123 match cx.tcx.hir().find(parent) {
124 Some(Node::Expr(expr)) => {
125 if let ExprKind::Loop(..) = expr.kind {
129 Some(Node::Block(block)) => {
130 let mut block_visitor = LoopNestVisitor {
133 nesting: Nesting::Unknown,
135 walk_block(&mut block_visitor, block);
136 if block_visitor.nesting == Nesting::RuledOut {
140 Some(Node::Stmt(_)) => (),
149 struct VarUsedAfterLoopVisitor {
152 past_while_let: bool,
153 var_used_after_while_let: bool,
156 impl<'tcx> Visitor<'tcx> for VarUsedAfterLoopVisitor {
157 type Map = Map<'tcx>;
159 fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
160 if self.past_while_let {
161 if path_to_local_id(expr, self.def_id) {
162 self.var_used_after_while_let = true;
164 } else if self.iter_expr_id == expr.hir_id {
165 self.past_while_let = true;
167 walk_expr(self, expr);
169 fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
170 NestedVisitorMap::None