Skip to content

Commit ec105ba

Browse files
fix: redundant_clone FP in overlapping lifetime (#14237)
fixes #13900 changelog: [`redundant_clone`]: fix FP in overlapping lifetime
2 parents ac4c69f + 07d8a9d commit ec105ba

File tree

3 files changed

+70
-8
lines changed

3 files changed

+70
-8
lines changed

clippy_lints/src/redundant_clone.rs

+36-8
Original file line numberDiff line numberDiff line change
@@ -182,15 +182,20 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
182182

183183
let clone_usage = if local == ret_local {
184184
CloneUsage {
185-
cloned_used: false,
185+
cloned_use_loc: None.into(),
186186
cloned_consume_or_mutate_loc: None,
187187
clone_consumed_or_mutated: true,
188188
}
189189
} else {
190190
let clone_usage = visit_clone_usage(local, ret_local, mir, bb);
191-
if clone_usage.cloned_used && clone_usage.clone_consumed_or_mutated {
191+
if clone_usage.cloned_use_loc.maybe_used() && clone_usage.clone_consumed_or_mutated {
192192
// cloned value is used, and the clone is modified or moved
193193
continue;
194+
} else if let MirLocalUsage::Used(loc) = clone_usage.cloned_use_loc
195+
&& possible_borrower.local_is_alive_at(ret_local, loc)
196+
{
197+
// cloned value is used, and the clone is alive.
198+
continue;
194199
} else if let Some(loc) = clone_usage.cloned_consume_or_mutate_loc
195200
// cloned value is mutated, and the clone is alive.
196201
&& possible_borrower.local_is_alive_at(ret_local, loc)
@@ -227,7 +232,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
227232

228233
span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |diag| {
229234
diag.span_suggestion(sugg_span, "remove this", "", app);
230-
if clone_usage.cloned_used {
235+
if clone_usage.cloned_use_loc.maybe_used() {
231236
diag.span_note(span, "cloned value is neither consumed nor mutated");
232237
} else {
233238
diag.span_note(
@@ -328,10 +333,33 @@ fn base_local_and_movability<'tcx>(
328333
(place.local, deref || field || slice)
329334
}
330335

331-
#[derive(Default)]
336+
#[derive(Debug, Default)]
337+
enum MirLocalUsage {
338+
/// The local maybe used, but we are not sure how.
339+
Unknown,
340+
/// The local is not used.
341+
#[default]
342+
Unused,
343+
/// The local is used at a specific location.
344+
Used(mir::Location),
345+
}
346+
347+
impl MirLocalUsage {
348+
fn maybe_used(&self) -> bool {
349+
matches!(self, MirLocalUsage::Unknown | MirLocalUsage::Used(_))
350+
}
351+
}
352+
353+
impl From<Option<mir::Location>> for MirLocalUsage {
354+
fn from(loc: Option<mir::Location>) -> Self {
355+
loc.map_or(MirLocalUsage::Unused, MirLocalUsage::Used)
356+
}
357+
}
358+
359+
#[derive(Debug, Default)]
332360
struct CloneUsage {
333-
/// Whether the cloned value is used after the clone.
334-
cloned_used: bool,
361+
/// The first location where the cloned value is used, if any.
362+
cloned_use_loc: MirLocalUsage,
335363
/// The first location where the cloned value is consumed or mutated, if any.
336364
cloned_consume_or_mutate_loc: Option<mir::Location>,
337365
/// Whether the clone value is mutated.
@@ -359,7 +387,7 @@ fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>,
359387
.map(|mut vec| (vec.remove(0), vec.remove(0)))
360388
{
361389
CloneUsage {
362-
cloned_used: !cloned_use_locs.is_empty(),
390+
cloned_use_loc: cloned_use_locs.first().copied().into(),
363391
cloned_consume_or_mutate_loc: cloned_consume_or_mutate_locs.first().copied(),
364392
// Consider non-temporary clones consumed.
365393
// TODO: Actually check for mutation of non-temporaries.
@@ -368,7 +396,7 @@ fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>,
368396
}
369397
} else {
370398
CloneUsage {
371-
cloned_used: true,
399+
cloned_use_loc: MirLocalUsage::Unknown,
372400
cloned_consume_or_mutate_loc: None,
373401
clone_consumed_or_mutated: true,
374402
}

tests/ui/redundant_clone.fixed

+17
Original file line numberDiff line numberDiff line change
@@ -274,3 +274,20 @@ mod issue10074 {
274274
println!("{v}");
275275
}
276276
}
277+
278+
mod issue13900 {
279+
use std::fmt::Display;
280+
281+
fn do_something(f: impl Display + Clone) -> String {
282+
let g = f.clone();
283+
format!("{} + {}", f, g)
284+
}
285+
286+
fn regression() {
287+
let mut a = String::new();
288+
let mut b = String::new();
289+
for _ in 1..10 {
290+
b = a.clone();
291+
}
292+
}
293+
}

tests/ui/redundant_clone.rs

+17
Original file line numberDiff line numberDiff line change
@@ -274,3 +274,20 @@ mod issue10074 {
274274
println!("{v}");
275275
}
276276
}
277+
278+
mod issue13900 {
279+
use std::fmt::Display;
280+
281+
fn do_something(f: impl Display + Clone) -> String {
282+
let g = f.clone();
283+
format!("{} + {}", f, g)
284+
}
285+
286+
fn regression() {
287+
let mut a = String::new();
288+
let mut b = String::new();
289+
for _ in 1..10 {
290+
b = a.clone();
291+
}
292+
}
293+
}

0 commit comments

Comments
 (0)