1 use super::WHILE_LET_ON_ITERATOR
;
2 use clippy_utils
::diagnostics
::span_lint_and_sugg
;
3 use clippy_utils
::source
::snippet_with_applicability
;
5 get_enclosing_loop_or_closure
, is_refutable
, is_trait_method
, match_def_path
, paths
, visitors
::is_res_used
,
7 use if_chain
::if_chain
;
8 use rustc_errors
::Applicability
;
9 use rustc_hir
::intravisit
::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}
;
10 use rustc_hir
::{def::Res, Expr, ExprKind, HirId, Local, MatchSource, Node, PatKind, QPath, UnOp}
;
11 use rustc_lint
::LateContext
;
12 use rustc_span
::{symbol::sym, Span, Symbol}
;
14 pub(super) fn check(cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'_
>) {
15 let (scrutinee_expr
, iter_expr
, some_pat
, loop_expr
) = if_chain
! {
16 if let ExprKind
::Match(scrutinee_expr
, [arm
, _
], MatchSource
::WhileLetDesugar
) = expr
.kind
;
17 // check for `Some(..)` pattern
18 if let PatKind
::TupleStruct(QPath
::Resolved(None
, pat_path
), some_pat
, _
) = arm
.pat
.kind
;
19 if let Res
::Def(_
, pat_did
) = pat_path
.res
;
20 if match_def_path(cx
, pat_did
, &paths
::OPTION_SOME
);
21 // check for call to `Iterator::next`
22 if let ExprKind
::MethodCall(method_name
, _
, [iter_expr
], _
) = scrutinee_expr
.kind
;
23 if method_name
.ident
.name
== sym
::next
;
24 if is_trait_method(cx
, scrutinee_expr
, sym
::Iterator
);
25 if let Some(iter_expr
) = try_parse_iter_expr(cx
, iter_expr
);
26 // get the loop containing the match expression
27 if let Some((_
, Node
::Expr(loop_expr
))) = cx
.tcx
.hir().parent_iter(expr
.hir_id
).nth(1);
28 if !uses_iter(cx
, &iter_expr
, arm
.body
);
30 (scrutinee_expr
, iter_expr
, some_pat
, loop_expr
)
36 let mut applicability
= Applicability
::MachineApplicable
;
37 let loop_var
= if let Some(some_pat
) = some_pat
.first() {
38 if is_refutable(cx
, some_pat
) {
39 // Refutable patterns don't work with for loops.
42 snippet_with_applicability(cx
, some_pat
.span
, "..", &mut applicability
)
47 // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be
48 // borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used
49 // afterwards a mutable borrow of a field isn't necessary.
50 let ref_mut
= if !iter_expr
.fields
.is_empty() || needs_mutable_borrow(cx
, &iter_expr
, loop_expr
) {
56 let iterator
= snippet_with_applicability(cx
, iter_expr
.span
, "_", &mut applicability
);
59 WHILE_LET_ON_ITERATOR
,
60 expr
.span
.with_hi(scrutinee_expr
.span
.hi()),
61 "this loop could be written as a `for` loop",
63 format
!("for {} in {}{}", loop_var
, ref_mut
, iterator
),
70 /// The span of the whole expression, not just the path and fields stored here.
72 /// The fields used, in order of child to parent.
74 /// The path being used.
77 /// Parses any expression to find out which field of which variable is used. Will return `None` if
78 /// the expression might have side effects.
79 fn try_parse_iter_expr(cx
: &LateContext
<'_
>, mut e
: &Expr
<'_
>) -> Option
<IterExpr
> {
81 let mut fields
= Vec
::new();
84 ExprKind
::Path(ref path
) => {
88 path
: cx
.qpath_res(path
, e
.hir_id
),
91 ExprKind
::Field(base
, name
) => {
92 fields
.push(name
.name
);
95 // Dereferencing a pointer has no side effects and doesn't affect which field is being used.
96 ExprKind
::Unary(UnOp
::Deref
, base
) if cx
.typeck_results().expr_ty(base
).is_ref() => e
= base
,
98 // Shouldn't have side effects, but there's no way to trace which field is used. So forget which fields have
100 ExprKind
::Index(base
, idx
) if !idx
.can_have_side_effects() => {
104 ExprKind
::Unary(UnOp
::Deref
, base
) => {
109 // No effect and doesn't affect which field is being used.
110 ExprKind
::DropTemps(base
) | ExprKind
::AddrOf(_
, _
, base
) | ExprKind
::Type(base
, _
) => e
= base
,
116 fn is_expr_same_field(cx
: &LateContext
<'_
>, mut e
: &Expr
<'_
>, mut fields
: &[Symbol
], path_res
: Res
) -> bool
{
118 match (&e
.kind
, fields
) {
119 (&ExprKind
::Field(base
, name
), [head_field
, tail_fields @
..]) if name
.name
== *head_field
=> {
121 fields
= tail_fields
;
123 (ExprKind
::Path(path
), []) => {
124 break cx
.qpath_res(path
, e
.hir_id
) == path_res
;
126 (&(ExprKind
::DropTemps(base
) | ExprKind
::AddrOf(_
, _
, base
) | ExprKind
::Type(base
, _
)), _
) => e
= base
,
132 /// Checks if the given expression is the same field as, is a child of, or is the parent of the
133 /// given field. Used to check if the expression can be used while the given field is borrowed
134 /// mutably. e.g. if checking for `x.y`, then `x.y`, `x.y.z`, and `x` will all return true, but
135 /// `x.z`, and `y` will return false.
136 fn is_expr_same_child_or_parent_field(cx
: &LateContext
<'_
>, expr
: &Expr
<'_
>, fields
: &[Symbol
], path_res
: Res
) -> bool
{
138 ExprKind
::Field(base
, name
) => {
139 if let Some((head_field
, tail_fields
)) = fields
.split_first() {
140 if name
.name
== *head_field
&& is_expr_same_field(cx
, base
, fields
, path_res
) {
143 // Check if the expression is a parent field
144 let mut fields_iter
= tail_fields
.iter();
145 while let Some(field
) = fields_iter
.next() {
146 if *field
== name
.name
&& is_expr_same_field(cx
, base
, fields_iter
.as_slice(), path_res
) {
152 // Check if the expression is a child field.
156 ExprKind
::Field(..) if is_expr_same_field(cx
, e
, fields
, path_res
) => break true,
157 ExprKind
::Field(base
, _
) | ExprKind
::DropTemps(base
) | ExprKind
::Type(base
, _
) => e
= base
,
158 ExprKind
::Path(ref path
) if fields
.is_empty() => {
159 break cx
.qpath_res(path
, e
.hir_id
) == path_res
;
165 // If the path matches, this is either an exact match, or the expression is a parent of the field.
166 ExprKind
::Path(ref path
) => cx
.qpath_res(path
, expr
.hir_id
) == path_res
,
167 ExprKind
::DropTemps(base
) | ExprKind
::Type(base
, _
) | ExprKind
::AddrOf(_
, _
, base
) => {
168 is_expr_same_child_or_parent_field(cx
, base
, fields
, path_res
)
174 /// Strips off all field and path expressions. This will return true if a field or path has been
175 /// skipped. Used to skip them after failing to check for equality.
176 fn skip_fields_and_path(expr
: &'tcx Expr
<'_
>) -> (Option
<&'tcx Expr
<'tcx
>>, bool
) {
180 ExprKind
::Field(base
, _
) | ExprKind
::DropTemps(base
) | ExprKind
::Type(base
, _
) => e
= base
,
181 ExprKind
::Path(_
) => return (None
, true),
185 (Some(e
), e
.hir_id
!= expr
.hir_id
)
188 /// Checks if the given expression uses the iterator.
189 fn uses_iter(cx
: &LateContext
<'tcx
>, iter_expr
: &IterExpr
, container
: &'tcx Expr
<'_
>) -> bool
{
190 struct V
<'a
, 'b
, 'tcx
> {
191 cx
: &'a LateContext
<'tcx
>,
192 iter_expr
: &'b IterExpr
,
195 impl Visitor
<'tcx
> for V
<'_
, '_
, 'tcx
> {
196 type Map
= ErasedMap
<'tcx
>;
197 fn nested_visit_map(&mut self) -> NestedVisitorMap
<Self::Map
> {
198 NestedVisitorMap
::None
201 fn visit_expr(&mut self, e
: &'tcx Expr
<'_
>) {
204 } else if is_expr_same_child_or_parent_field(self.cx
, e
, &self.iter_expr
.fields
, self.iter_expr
.path
) {
205 self.uses_iter
= true;
206 } else if let (e
, true) = skip_fields_and_path(e
) {
210 } else if let ExprKind
::Closure(_
, _
, id
, _
, _
) = e
.kind
{
211 if is_res_used(self.cx
, self.iter_expr
.path
, id
) {
212 self.uses_iter
= true;
225 v
.visit_expr(container
);
229 #[allow(clippy::too_many_lines)]
230 fn needs_mutable_borrow(cx
: &LateContext
<'tcx
>, iter_expr
: &IterExpr
, loop_expr
: &'tcx Expr
<'_
>) -> bool
{
231 struct AfterLoopVisitor
<'a
, 'b
, 'tcx
> {
232 cx
: &'a LateContext
<'tcx
>,
233 iter_expr
: &'b IterExpr
,
238 impl Visitor
<'tcx
> for AfterLoopVisitor
<'_
, '_
, 'tcx
> {
239 type Map
= ErasedMap
<'tcx
>;
240 fn nested_visit_map(&mut self) -> NestedVisitorMap
<Self::Map
> {
241 NestedVisitorMap
::None
244 fn visit_expr(&mut self, e
: &'tcx Expr
<'_
>) {
249 if is_expr_same_child_or_parent_field(self.cx
, e
, &self.iter_expr
.fields
, self.iter_expr
.path
) {
250 self.used_iter
= true;
251 } else if let (e
, true) = skip_fields_and_path(e
) {
255 } else if let ExprKind
::Closure(_
, _
, id
, _
, _
) = e
.kind
{
256 self.used_iter
= is_res_used(self.cx
, self.iter_expr
.path
, id
);
260 } else if self.loop_id
== e
.hir_id
{
261 self.after_loop
= true;
268 struct NestedLoopVisitor
<'a
, 'b
, 'tcx
> {
269 cx
: &'a LateContext
<'tcx
>,
270 iter_expr
: &'b IterExpr
,
277 impl Visitor
<'tcx
> for NestedLoopVisitor
<'a
, 'b
, 'tcx
> {
278 type Map
= ErasedMap
<'tcx
>;
279 fn nested_visit_map(&mut self) -> NestedVisitorMap
<Self::Map
> {
280 NestedVisitorMap
::None
283 fn visit_local(&mut self, l
: &'tcx Local
<'_
>) {
284 if !self.after_loop
{
285 l
.pat
.each_binding_or_first(&mut |_
, id
, _
, _
| {
286 if id
== self.local_id
{
287 self.found_local
= true;
291 if let Some(e
) = l
.init
{
296 fn visit_expr(&mut self, e
: &'tcx Expr
<'_
>) {
301 if is_expr_same_child_or_parent_field(self.cx
, e
, &self.iter_expr
.fields
, self.iter_expr
.path
) {
302 self.used_after
= true;
303 } else if let (e
, true) = skip_fields_and_path(e
) {
307 } else if let ExprKind
::Closure(_
, _
, id
, _
, _
) = e
.kind
{
308 self.used_after
= is_res_used(self.cx
, self.iter_expr
.path
, id
);
312 } else if e
.hir_id
== self.loop_id
{
313 self.after_loop
= true;
320 if let Some(e
) = get_enclosing_loop_or_closure(cx
.tcx
, loop_expr
) {
321 // The iterator expression will be used on the next iteration (for loops), or on the next call (for
322 // closures) unless it is declared within the enclosing expression. TODO: Check for closures
323 // used where an `FnOnce` type is expected.
324 let local_id
= match iter_expr
.path
{
325 Res
::Local(id
) => id
,
328 let mut v
= NestedLoopVisitor
{
332 loop_id
: loop_expr
.hir_id
,
338 v
.used_after
|| !v
.found_local
340 let mut v
= AfterLoopVisitor
{
343 loop_id
: loop_expr
.hir_id
,
347 v
.visit_expr(&cx
.tcx
.hir().body(cx
.enclosing_body
.unwrap()).value
);