Check for mutation

This commit is contained in:
Shotaro Yamada 2020-03-13 01:25:18 +09:00
parent a377378528
commit aca64b8df7
4 changed files with 27 additions and 10 deletions

View file

@ -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;
},
_ => {},
}
}
}
}

View file

@ -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");
}

View file

@ -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");
}

View file

@ -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");