Skip to content

Commit

Permalink
Check for mutation
Browse files Browse the repository at this point in the history
  • Loading branch information
sinkuu committed Mar 12, 2020
1 parent a377378 commit aca64b8
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 10 deletions.
25 changes: 16 additions & 9 deletions clippy_lints/src/redundant_clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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]
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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 {
Expand All @@ -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;
},
_ => {},
}
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions tests/ui/redundant_clone.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
5 changes: 5 additions & 0 deletions tests/ui/redundant_clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
2 changes: 1 addition & 1 deletion tests/ui/redundant_clone.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down

0 comments on commit aca64b8

Please sign in to comment.