Skip to content

Commit f0f6f1c

Browse files
committed
CloneUsage -> LocalUsage in mir.rs
1 parent e40f4a5 commit f0f6f1c

File tree

2 files changed

+90
-170
lines changed
  • clippy_lints/src/redundant_clone
  • clippy_utils/src

2 files changed

+90
-170
lines changed

clippy_lints/src/redundant_clone/mod.rs

Lines changed: 36 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
2+
use clippy_utils::mir::{visit_local_usage, LocalUsage};
23
use clippy_utils::source::snippet_opt;
34
use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth};
45
use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths};
@@ -7,10 +8,7 @@ use rustc_errors::Applicability;
78
use rustc_hir::intravisit::FnKind;
89
use rustc_hir::{def_id, Body, FnDecl, HirId};
910
use rustc_lint::{LateContext, LateLintPass};
10-
use rustc_middle::mir::{
11-
self, traversal,
12-
visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _},
13-
};
11+
use rustc_middle::mir::{self, visit::Visitor as _};
1412
use rustc_middle::ty::{self, Ty};
1513
use rustc_mir_dataflow::Analysis;
1614
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -381,86 +379,42 @@ struct CloneUsage {
381379
/// Whether the clone value is mutated.
382380
clone_consumed_or_mutated: bool,
383381
}
384-
fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, bb: mir::BasicBlock) -> CloneUsage {
385-
struct V {
386-
cloned: mir::Local,
387-
clone: mir::Local,
388-
result: CloneUsage,
389-
}
390-
impl<'tcx> mir::visit::Visitor<'tcx> for V {
391-
fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) {
392-
let statements = &data.statements;
393-
for (statement_index, statement) in statements.iter().enumerate() {
394-
self.visit_statement(statement, mir::Location { block, statement_index });
395-
}
396382

397-
self.visit_terminator(
398-
data.terminator(),
399-
mir::Location {
400-
block,
401-
statement_index: statements.len(),
402-
},
403-
);
383+
fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, bb: mir::BasicBlock) -> CloneUsage {
384+
if let Some(
385+
&[
386+
LocalUsage {
387+
local_use_loc: cloned_use_loc,
388+
local_consume_or_mutate_loc: cloned_consume_or_mutate_loc,
389+
},
390+
LocalUsage {
391+
local_use_loc: _,
392+
local_consume_or_mutate_loc: clone_consume_or_mutate_loc,
393+
},
394+
],
395+
) = visit_local_usage(
396+
&[cloned, clone],
397+
mir,
398+
mir::Location {
399+
block: bb,
400+
statement_index: mir.basic_blocks()[bb].statements.len(),
401+
},
402+
)
403+
.as_deref()
404+
{
405+
CloneUsage {
406+
cloned_used: cloned_use_loc.is_some(),
407+
cloned_consume_or_mutate_loc,
408+
// Consider non-temporary clones consumed.
409+
// TODO: Actually check for mutation of non-temporaries.
410+
clone_consumed_or_mutated: mir.local_kind(clone) != mir::LocalKind::Temp
411+
|| clone_consume_or_mutate_loc.is_some(),
404412
}
405-
406-
fn visit_place(&mut self, place: &mir::Place<'tcx>, ctx: PlaceContext, loc: mir::Location) {
407-
let local = place.local;
408-
409-
if local == self.cloned
410-
&& !matches!(
411-
ctx,
412-
PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_)
413-
)
414-
{
415-
self.result.cloned_used = true;
416-
self.result.cloned_consume_or_mutate_loc = self.result.cloned_consume_or_mutate_loc.or_else(|| {
417-
matches!(
418-
ctx,
419-
PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)
420-
| PlaceContext::MutatingUse(MutatingUseContext::Borrow)
421-
)
422-
.then(|| loc)
423-
});
424-
} else if local == self.clone {
425-
match ctx {
426-
PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)
427-
| PlaceContext::MutatingUse(MutatingUseContext::Borrow) => {
428-
self.result.clone_consumed_or_mutated = true;
429-
},
430-
_ => {},
431-
}
432-
}
413+
} else {
414+
CloneUsage {
415+
cloned_used: true,
416+
cloned_consume_or_mutate_loc: None,
417+
clone_consumed_or_mutated: true,
433418
}
434419
}
435-
436-
let init = CloneUsage {
437-
cloned_used: false,
438-
cloned_consume_or_mutate_loc: None,
439-
// Consider non-temporary clones consumed.
440-
// TODO: Actually check for mutation of non-temporaries.
441-
clone_consumed_or_mutated: mir.local_kind(clone) != mir::LocalKind::Temp,
442-
};
443-
traversal::ReversePostorder::new(mir, bb)
444-
.skip(1)
445-
.fold(init, |usage, (tbb, tdata)| {
446-
// Short-circuit
447-
if (usage.cloned_used && usage.clone_consumed_or_mutated) ||
448-
// Give up on loops
449-
tdata.terminator().successors().any(|s| s == bb)
450-
{
451-
return CloneUsage {
452-
cloned_used: true,
453-
clone_consumed_or_mutated: true,
454-
..usage
455-
};
456-
}
457-
458-
let mut v = V {
459-
cloned,
460-
clone,
461-
result: usage,
462-
};
463-
v.visit_basic_block_data(tbb, tdata);
464-
v.result
465-
})
466420
}

clippy_utils/src/mir.rs

Lines changed: 54 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,70 @@
1-
use rustc_middle::mir::{
2-
self, traversal,
3-
visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _},
4-
};
1+
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
2+
use rustc_middle::mir::{traversal, Body, Local, Location, Place};
53

6-
#[derive(Default)]
7-
pub struct CloneUsage {
8-
/// Whether the cloned value is used after the clone.
9-
pub cloned_used: bool,
10-
/// The first location where the cloned value is consumed or mutated, if any.
11-
pub cloned_consume_or_mutate_loc: Option<mir::Location>,
12-
/// Whether the clone value is mutated.
13-
pub clone_consumed_or_mutated: bool,
4+
#[derive(Clone, Default)]
5+
pub struct LocalUsage {
6+
/// The first location where the local is used, if any.
7+
pub local_use_loc: Option<Location>,
8+
/// The first location where the local is consumed or mutated, if any.
9+
pub local_consume_or_mutate_loc: Option<Location>,
1410
}
1511

16-
pub fn visit_clone_usage(
17-
cloned: mir::Local,
18-
clone: mir::Local,
19-
mir: &mir::Body<'_>,
20-
bb: mir::BasicBlock,
21-
) -> CloneUsage {
22-
let init = CloneUsage {
23-
cloned_used: false,
24-
cloned_consume_or_mutate_loc: None,
25-
// Consider non-temporary clones consumed.
26-
// TODO: Actually check for mutation of non-temporaries.
27-
clone_consumed_or_mutated: mir.local_kind(clone) != mir::LocalKind::Temp,
28-
};
12+
pub fn visit_local_usage(locals: &[Local], mir: &Body<'_>, location: Location) -> Option<Vec<LocalUsage>> {
13+
let init = vec![
14+
LocalUsage {
15+
local_use_loc: None,
16+
local_consume_or_mutate_loc: None,
17+
};
18+
locals.len()
19+
];
2920

30-
traversal::ReversePostorder::new(mir, bb)
31-
.skip(1)
32-
.fold(init, |usage, (tbb, tdata)| {
33-
// Short-circuit
34-
if (usage.cloned_used && usage.clone_consumed_or_mutated) ||
35-
// Give up on loops
36-
tdata.terminator().successors().any(|s| s == bb)
37-
{
38-
return CloneUsage {
39-
cloned_used: true,
40-
clone_consumed_or_mutated: true,
41-
..usage
42-
};
43-
}
21+
traversal::ReversePostorder::new(mir, location.block).try_fold(init, |usage, (tbb, tdata)| {
22+
// Give up on loops
23+
if tdata.terminator().successors().any(|s| s == location.block) {
24+
return None;
25+
}
4426

45-
let mut v = V {
46-
cloned,
47-
clone,
48-
result: usage,
49-
};
50-
v.visit_basic_block_data(tbb, tdata);
51-
v.result
52-
})
27+
let mut v = V {
28+
locals,
29+
location,
30+
result: usage,
31+
};
32+
v.visit_basic_block_data(tbb, tdata);
33+
Some(v.result)
34+
})
5335
}
5436

55-
struct V {
56-
cloned: mir::Local,
57-
clone: mir::Local,
58-
result: CloneUsage,
37+
struct V<'a> {
38+
locals: &'a [Local],
39+
location: Location,
40+
result: Vec<LocalUsage>,
5941
}
6042

61-
impl<'tcx> mir::visit::Visitor<'tcx> for V {
62-
fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) {
63-
let statements = &data.statements;
64-
for (statement_index, statement) in statements.iter().enumerate() {
65-
self.visit_statement(statement, mir::Location { block, statement_index });
43+
impl<'a, 'tcx> Visitor<'tcx> for V<'a> {
44+
fn visit_place(&mut self, place: &Place<'tcx>, ctx: PlaceContext, loc: Location) {
45+
if loc.block == self.location.block && loc.statement_index <= self.location.statement_index {
46+
return;
6647
}
6748

68-
self.visit_terminator(
69-
data.terminator(),
70-
mir::Location {
71-
block,
72-
statement_index: statements.len(),
73-
},
74-
);
75-
}
76-
77-
fn visit_place(&mut self, place: &mir::Place<'tcx>, ctx: PlaceContext, loc: mir::Location) {
7849
let local = place.local;
7950

80-
if local == self.cloned
81-
&& !matches!(
82-
ctx,
83-
PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_)
84-
)
85-
{
86-
self.result.cloned_used = true;
87-
self.result.cloned_consume_or_mutate_loc = self.result.cloned_consume_or_mutate_loc.or_else(|| {
88-
matches!(
89-
ctx,
90-
PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)
91-
| PlaceContext::MutatingUse(MutatingUseContext::Borrow)
92-
)
93-
.then(|| loc)
94-
});
95-
} else if local == self.clone {
96-
match ctx {
97-
PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)
98-
| PlaceContext::MutatingUse(MutatingUseContext::Borrow) => {
99-
self.result.clone_consumed_or_mutated = true;
100-
},
101-
_ => {},
51+
for (i, self_local) in self.locals.iter().enumerate() {
52+
if local == *self_local {
53+
self.result[i].local_use_loc = self.result[i].local_use_loc.or_else(|| {
54+
(!matches!(
55+
ctx,
56+
PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_)
57+
))
58+
.then_some(loc)
59+
});
60+
self.result[i].local_consume_or_mutate_loc = self.result[i].local_consume_or_mutate_loc.or_else(|| {
61+
matches!(
62+
ctx,
63+
PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)
64+
| PlaceContext::MutatingUse(MutatingUseContext::Borrow)
65+
)
66+
.then_some(loc)
67+
});
10268
}
10369
}
10470
}

0 commit comments

Comments
 (0)