1 use super::utils
::{LoopNestVisitor, Nesting}
;
2 use super::WHILE_LET_ON_ITERATOR
;
3 use crate::utils
::usage
::mutated_variables
;
5 get_enclosing_block
, get_trait_def_id
, implements_trait
, is_refutable
, last_path_segment
, match_trait_method
,
6 path_to_local
, path_to_local_id
, paths
, snippet_with_applicability
, span_lint_and_sugg
,
8 use if_chain
::if_chain
;
9 use rustc_errors
::Applicability
;
10 use rustc_hir
::intravisit
::{walk_block, walk_expr, NestedVisitorMap, Visitor}
;
11 use rustc_hir
::{Expr, ExprKind, HirId, MatchSource, Node, PatKind}
;
12 use rustc_lint
::LateContext
;
13 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
) = get_trait_def_id(cx
, &paths
::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 && match_trait_method(cx
, match_expr
, &paths
::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