about summary refs log tree commit diff
diff options
context:
space:
mode:
authorShotaro Yamada <sinkuu@sinkuu.xyz>2020-03-13 01:25:18 +0900
committerShotaro Yamada <sinkuu@sinkuu.xyz>2020-03-13 01:25:18 +0900
commitaca64b8df77670c6661b7174d7edc16133442162 (patch)
tree2a3e49ac8d0c3e8cf5f181d7b23018b5c5cf6d27
parenta3773785288e71fde264b9264a142a662aace5ee (diff)
downloadrust-aca64b8df77670c6661b7174d7edc16133442162.tar.gz
rust-aca64b8df77670c6661b7174d7edc16133442162.zip
Check for mutation
-rw-r--r--clippy_lints/src/redundant_clone.rs25
-rw-r--r--tests/ui/redundant_clone.fixed5
-rw-r--r--tests/ui/redundant_clone.rs5
-rw-r--r--tests/ui/redundant_clone.stderr2
4 files changed, 27 insertions, 10 deletions
diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs
index 7c1a7040846..624fe227ca3 100644
--- a/clippy_lints/src/redundant_clone.rs
+++ b/clippy_lints/src/redundant_clone.rs
@@ -195,8 +195,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
             let is_temp = mir_read_only.local_kind(ret_local) == mir::LocalKind::Temp;
 
             // 1. `local` can be moved out if it is not used later.
-            // 2. If `ret_local` is a temporary and is not consumed, we can remove this `clone` call anyway.
-            let (used, consumed) = traversal::ReversePostorder::new(&mir, bb).skip(1).fold(
+            // 2. If `ret_local` is a temporary and is neither consumed nor mutated, we can remove this `clone`
+            // call anyway.
+            let (used, consumed_or_mutated) = traversal::ReversePostorder::new(&mir, bb).skip(1).fold(
                 (false, !is_temp),
                 |(used, consumed), (tbb, tdata)| {
                     // Short-circuit
@@ -209,14 +210,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
 
                     let mut vis = LocalUseVisitor {
                         used: (local, false),
-                        consumed: (ret_local, false),
+                        consumed_or_mutated: (ret_local, false),
                     };
                     vis.visit_basic_block_data(tbb, tdata);
-                    (used || vis.used.1, consumed || vis.consumed.1)
+                    (used || vis.used.1, consumed || vis.consumed_or_mutated.1)
                 },
             );
 
-            if !used || !consumed {
+            if !used || !consumed_or_mutated {
                 let span = terminator.source_info.span;
                 let scope = terminator.source_info.scope;
                 let node = mir.source_scopes[scope]
@@ -253,7 +254,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
                             if used {
                                 db.span_note(
                                     span,
-                                    "cloned value is not consumed",
+                                    "cloned value is neither consumed nor mutated",
                                 );
                             } else {
                                 db.span_note(
@@ -355,7 +356,7 @@ fn base_local_and_movability<'tcx>(
 
 struct LocalUseVisitor {
     used: (mir::Local, bool),
-    consumed: (mir::Local, bool),
+    consumed_or_mutated: (mir::Local, bool),
 }
 
 impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
@@ -381,8 +382,14 @@ impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
             self.used.1 = true;
         }
 
-        if *local == self.consumed.0 && matches!(ctx, PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)) {
-            self.consumed.1 = true;
+        if *local == self.consumed_or_mutated.0 {
+            match ctx {
+                PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)
+                | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => {
+                    self.consumed_or_mutated.1 = true;
+                },
+                _ => {},
+            }
         }
     }
 }
diff --git a/tests/ui/redundant_clone.fixed b/tests/ui/redundant_clone.fixed
index 99d97f9cc3c..17734b04aba 100644
--- a/tests/ui/redundant_clone.fixed
+++ b/tests/ui/redundant_clone.fixed
@@ -145,4 +145,9 @@ fn not_consumed() {
     // redundant. (It also does not consume the PathBuf)
 
     println!("x: {:?}, y: {:?}", x, y);
+
+    let mut s = String::new();
+    s.clone().push_str("foo"); // OK, removing this `clone()` will change the behavior.
+    s.push_str("bar");
+    assert_eq!(s, "bar");
 }
diff --git a/tests/ui/redundant_clone.rs b/tests/ui/redundant_clone.rs
index 58cd05c67cf..aee6855eea9 100644
--- a/tests/ui/redundant_clone.rs
+++ b/tests/ui/redundant_clone.rs
@@ -145,4 +145,9 @@ fn not_consumed() {
     // redundant. (It also does not consume the PathBuf)
 
     println!("x: {:?}, y: {:?}", x, y);
+
+    let mut s = String::new();
+    s.clone().push_str("foo"); // OK, removing this `clone()` will change the behavior.
+    s.push_str("bar");
+    assert_eq!(s, "bar");
 }
diff --git a/tests/ui/redundant_clone.stderr b/tests/ui/redundant_clone.stderr
index 04fe74e9175..9c27812b9cd 100644
--- a/tests/ui/redundant_clone.stderr
+++ b/tests/ui/redundant_clone.stderr
@@ -161,7 +161,7 @@ error: redundant clone
 LL |     let y = x.clone().join("matthias");
    |              ^^^^^^^^ help: remove this
    |
-note: cloned value is not consumed
+note: cloned value is neither consumed nor mutated
   --> $DIR/redundant_clone.rs:143:13
    |
 LL |     let y = x.clone().join("matthias");