about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2019-03-14 16:09:47 -0700
committerEsteban Küber <esteban@kuber.com.ar>2019-03-24 11:46:13 -0700
commitae883dc82641c9c80d6dc09926a12b984f76d579 (patch)
treef501181ab5bee319d85baa9f70d693c6aa003b59
parentc7b5f4d0f7ccf5d40168f541d4418bb76d9cb513 (diff)
downloadrust-ae883dc82641c9c80d6dc09926a12b984f76d579.tar.gz
rust-ae883dc82641c9c80d6dc09926a12b984f76d579.zip
When moving out of a for loop head, suggest borrowing it
When encountering code like the following, suggest borrowing the for loop
head to avoid moving it into the for loop pattern:

```
fn main() {
    let a = vec![1, 2, 3];
    for i in &a {
        for j in a {
            println!("{} * {} = {}", i, j, i * j);
        }
    }
}
```
-rw-r--r--src/librustc/hir/lowering.rs3
-rw-r--r--src/librustc_borrowck/borrowck/mod.rs14
-rw-r--r--src/test/ui/suggestions/borrow-for-loop-head.rs10
-rw-r--r--src/test/ui/suggestions/borrow-for-loop-head.stderr24
4 files changed, 50 insertions, 1 deletions
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 2251e67233c..0a7536d6301 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -4334,13 +4334,14 @@ impl<'a> LoweringContext<'a> {
                 //   }
 
                 // expand <head>
-                let head = self.lower_expr(head);
+                let mut head = self.lower_expr(head);
                 let head_sp = head.span;
                 let desugared_span = self.mark_span_with_reason(
                     CompilerDesugaringKind::ForLoop,
                     head_sp,
                     None,
                 );
+                head.span = desugared_span;
 
                 let iter = self.str_to_ident("iter");
 
diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs
index 4e1d360562d..2017d9bfe2c 100644
--- a/src/librustc_borrowck/borrowck/mod.rs
+++ b/src/librustc_borrowck/borrowck/mod.rs
@@ -34,6 +34,7 @@ use std::fmt;
 use std::rc::Rc;
 use rustc_data_structures::sync::Lrc;
 use std::hash::{Hash, Hasher};
+use syntax::source_map::CompilerDesugaringKind;
 use syntax_pos::{MultiSpan, Span};
 use errors::{Applicability, DiagnosticBuilder, DiagnosticId};
 use log::debug;
@@ -744,6 +745,19 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                 },
                 moved_lp.ty));
         }
+        if let (Some(CompilerDesugaringKind::ForLoop), Ok(snippet)) = (
+            move_span.compiler_desugaring_kind(),
+            self.tcx.sess.source_map().span_to_snippet(move_span),
+         ) {
+            if !snippet.starts_with("&") {
+                err.span_suggestion(
+                    move_span,
+                    "consider borrowing this to avoid moving it into the for loop",
+                    format!("&{}", snippet),
+                    Applicability::MaybeIncorrect,
+                );
+            }
+        }
 
         // Note: we used to suggest adding a `ref binding` or calling
         // `clone` but those suggestions have been removed because
diff --git a/src/test/ui/suggestions/borrow-for-loop-head.rs b/src/test/ui/suggestions/borrow-for-loop-head.rs
new file mode 100644
index 00000000000..c2bda55e589
--- /dev/null
+++ b/src/test/ui/suggestions/borrow-for-loop-head.rs
@@ -0,0 +1,10 @@
+fn main() {
+    let a = vec![1, 2, 3];
+    for i in &a {
+        for j in a {
+        //~^ ERROR cannot move out of `a` because it is borrowed
+        //~| ERROR use of moved value: `a`
+            println!("{} * {} = {}", i, j, i * j);
+        }
+    }
+}
diff --git a/src/test/ui/suggestions/borrow-for-loop-head.stderr b/src/test/ui/suggestions/borrow-for-loop-head.stderr
new file mode 100644
index 00000000000..17ac3fe86d0
--- /dev/null
+++ b/src/test/ui/suggestions/borrow-for-loop-head.stderr
@@ -0,0 +1,24 @@
+error[E0505]: cannot move out of `a` because it is borrowed
+  --> $DIR/borrow-for-loop-head.rs:4:18
+   |
+LL |     for i in &a {
+   |               - borrow of `a` occurs here
+LL |         for j in a {
+   |                  ^ move out of `a` occurs here
+
+error[E0382]: use of moved value: `a`
+  --> $DIR/borrow-for-loop-head.rs:4:18
+   |
+LL |         for j in a {
+   |                  ^ value moved here in previous iteration of loop
+   |
+   = note: move occurs because `a` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
+help: consider borrowing this to avoid moving it into the for loop
+   |
+LL |         for j in &a {
+   |                  ^^
+
+error: aborting due to 2 previous errors
+
+Some errors occurred: E0382, E0505.
+For more information about an error, try `rustc --explain E0382`.