about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-07-31 15:15:41 +0000
committerbors <bors@rust-lang.org>2021-07-31 15:15:41 +0000
commit05fa78fd3edaf19a2508db9a74dbaa4cb009f236 (patch)
tree4dc5e836ba83d56939fddd1abb6a6123b34d389f
parentf6a5889ffad5f819b80f07b40988ba2576f79296 (diff)
parent205aa88921ce63898f7fd3db6e9070c70987be2e (diff)
downloadrust-05fa78fd3edaf19a2508db9a74dbaa4cb009f236.tar.gz
rust-05fa78fd3edaf19a2508db9a74dbaa4cb009f236.zip
Auto merge of #7520 - Jarcho:while_let_7510, r=Manishearth
Fix `while_let_on_iterator` - #7510

fixes: #7510
changelog: Suggest re-borrowing mutable references in `while_let_on_iterator`
-rw-r--r--clippy_lints/src/loops/while_let_on_iterator.rs15
-rw-r--r--tests/ui/while_let_on_iterator.fixed32
-rw-r--r--tests/ui/while_let_on_iterator.rs32
-rw-r--r--tests/ui/while_let_on_iterator.stderr16
4 files changed, 90 insertions, 5 deletions
diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs
index d57588716a5..ef822e0cbe5 100644
--- a/clippy_lints/src/loops/while_let_on_iterator.rs
+++ b/clippy_lints/src/loops/while_let_on_iterator.rs
@@ -7,7 +7,7 @@ use clippy_utils::{
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor};
-use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, MatchSource, Node, PatKind, QPath, UnOp};
+use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, MatchSource, Mutability, Node, PatKind, QPath, UnOp};
 use rustc_lint::LateContext;
 use rustc_span::{symbol::sym, Span, Symbol};
 
@@ -48,7 +48,12 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
     // borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used
     // afterwards a mutable borrow of a field isn't necessary.
     let ref_mut = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) {
-        "&mut "
+        if cx.typeck_results().node_type(iter_expr.hir_id).ref_mutability() == Some(Mutability::Mut) {
+            // Reborrow for mutable references. It may not be possible to get a mutable reference here.
+            "&mut *"
+        } else {
+            "&mut "
+        }
     } else {
         ""
     };
@@ -69,6 +74,8 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 struct IterExpr {
     /// The span of the whole expression, not just the path and fields stored here.
     span: Span,
+    /// The HIR id of the whole expression, not just the path and fields stored here.
+    hir_id: HirId,
     /// The fields used, in order of child to parent.
     fields: Vec<Symbol>,
     /// The path being used.
@@ -78,12 +85,14 @@ struct IterExpr {
 /// the expression might have side effects.
 fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExpr> {
     let span = e.span;
+    let hir_id = e.hir_id;
     let mut fields = Vec::new();
     loop {
         match e.kind {
             ExprKind::Path(ref path) => {
                 break Some(IterExpr {
                     span,
+                    hir_id,
                     fields,
                     path: cx.qpath_res(path, e.hir_id),
                 });
@@ -137,7 +146,7 @@ fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fie
     match expr.kind {
         ExprKind::Field(base, name) => {
             if let Some((head_field, tail_fields)) = fields.split_first() {
-                if name.name == *head_field && is_expr_same_field(cx, base, fields, path_res) {
+                if name.name == *head_field && is_expr_same_field(cx, base, tail_fields, path_res) {
                     return true;
                 }
                 // Check if the expression is a parent field
diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed
index 52e80ceee83..cdcdd808c94 100644
--- a/tests/ui/while_let_on_iterator.fixed
+++ b/tests/ui/while_let_on_iterator.fixed
@@ -334,6 +334,38 @@ fn issue7249() {
     x();
 }
 
+fn issue7510() {
+    let mut it = 0..10;
+    let it = &mut it;
+    // Needs to reborrow `it` as the binding isn't mutable
+    for x in &mut *it {
+        if x % 2 == 0 {
+            break;
+        }
+    }
+    println!("{}", it.next().unwrap());
+
+    struct S<T>(T);
+    let mut it = 0..10;
+    let it = S(&mut it);
+    // Needs to reborrow `it.0` as the binding isn't mutable
+    for x in &mut *it.0 {
+        if x % 2 == 0 {
+            break;
+        }
+    }
+    println!("{}", it.0.next().unwrap());
+}
+
+fn exact_match_with_single_field() {
+    struct S<T>(T);
+    let mut s = S(0..10);
+    // Don't lint. `s.0` is used inside the loop.
+    while let Some(_) = s.0.next() {
+        let _ = &mut s.0;
+    }
+}
+
 fn main() {
     let mut it = 0..20;
     for _ in it {
diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs
index 5078a3c9028..72f34257d1f 100644
--- a/tests/ui/while_let_on_iterator.rs
+++ b/tests/ui/while_let_on_iterator.rs
@@ -334,6 +334,38 @@ fn issue7249() {
     x();
 }
 
+fn issue7510() {
+    let mut it = 0..10;
+    let it = &mut it;
+    // Needs to reborrow `it` as the binding isn't mutable
+    while let Some(x) = it.next() {
+        if x % 2 == 0 {
+            break;
+        }
+    }
+    println!("{}", it.next().unwrap());
+
+    struct S<T>(T);
+    let mut it = 0..10;
+    let it = S(&mut it);
+    // Needs to reborrow `it.0` as the binding isn't mutable
+    while let Some(x) = it.0.next() {
+        if x % 2 == 0 {
+            break;
+        }
+    }
+    println!("{}", it.0.next().unwrap());
+}
+
+fn exact_match_with_single_field() {
+    struct S<T>(T);
+    let mut s = S(0..10);
+    // Don't lint. `s.0` is used inside the loop.
+    while let Some(_) = s.0.next() {
+        let _ = &mut s.0;
+    }
+}
+
 fn main() {
     let mut it = 0..20;
     while let Some(..) = it.next() {
diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr
index cb0afeae15e..ff9b08996da 100644
--- a/tests/ui/while_let_on_iterator.stderr
+++ b/tests/ui/while_let_on_iterator.stderr
@@ -111,10 +111,22 @@ LL |         while let Some(x) = it.next() {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in &mut it`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:339:5
+  --> $DIR/while_let_on_iterator.rs:341:5
+   |
+LL |     while let Some(x) = it.next() {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in &mut *it`
+
+error: this loop could be written as a `for` loop
+  --> $DIR/while_let_on_iterator.rs:352:5
+   |
+LL |     while let Some(x) = it.0.next() {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in &mut *it.0`
+
+error: this loop could be written as a `for` loop
+  --> $DIR/while_let_on_iterator.rs:371:5
    |
 LL |     while let Some(..) = it.next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it`
 
-error: aborting due to 19 previous errors
+error: aborting due to 21 previous errors