Skip to content

Commit a39ce7d

Browse files
committed
Copy visit_clone_usage to clippy_utils/src/mir.rs
1 parent e43f5a1 commit a39ce7d

File tree

2 files changed

+103
-0
lines changed

2 files changed

+103
-0
lines changed

clippy_utils/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ pub mod eager_or_lazy;
4747
pub mod higher;
4848
mod hir_utils;
4949
pub mod macros;
50+
pub mod mir;
5051
pub mod msrvs;
5152
pub mod numeric_literal;
5253
pub mod paths;

clippy_utils/src/mir.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
use rustc_middle::mir::{
2+
self, traversal,
3+
visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _},
4+
};
5+
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,
14+
}
15+
pub fn visit_clone_usage(
16+
cloned: mir::Local,
17+
clone: mir::Local,
18+
mir: &mir::Body<'_>,
19+
bb: mir::BasicBlock,
20+
) -> CloneUsage {
21+
struct V {
22+
cloned: mir::Local,
23+
clone: mir::Local,
24+
result: CloneUsage,
25+
}
26+
impl<'tcx> mir::visit::Visitor<'tcx> for V {
27+
fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) {
28+
let statements = &data.statements;
29+
for (statement_index, statement) in statements.iter().enumerate() {
30+
self.visit_statement(statement, mir::Location { block, statement_index });
31+
}
32+
33+
self.visit_terminator(
34+
data.terminator(),
35+
mir::Location {
36+
block,
37+
statement_index: statements.len(),
38+
},
39+
);
40+
}
41+
42+
fn visit_place(&mut self, place: &mir::Place<'tcx>, ctx: PlaceContext, loc: mir::Location) {
43+
let local = place.local;
44+
45+
if local == self.cloned
46+
&& !matches!(
47+
ctx,
48+
PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_)
49+
)
50+
{
51+
self.result.cloned_used = true;
52+
self.result.cloned_consume_or_mutate_loc = self.result.cloned_consume_or_mutate_loc.or_else(|| {
53+
matches!(
54+
ctx,
55+
PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)
56+
| PlaceContext::MutatingUse(MutatingUseContext::Borrow)
57+
)
58+
.then(|| loc)
59+
});
60+
} else if local == self.clone {
61+
match ctx {
62+
PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)
63+
| PlaceContext::MutatingUse(MutatingUseContext::Borrow) => {
64+
self.result.clone_consumed_or_mutated = true;
65+
},
66+
_ => {},
67+
}
68+
}
69+
}
70+
}
71+
72+
let init = CloneUsage {
73+
cloned_used: false,
74+
cloned_consume_or_mutate_loc: None,
75+
// Consider non-temporary clones consumed.
76+
// TODO: Actually check for mutation of non-temporaries.
77+
clone_consumed_or_mutated: mir.local_kind(clone) != mir::LocalKind::Temp,
78+
};
79+
traversal::ReversePostorder::new(mir, bb)
80+
.skip(1)
81+
.fold(init, |usage, (tbb, tdata)| {
82+
// Short-circuit
83+
if (usage.cloned_used && usage.clone_consumed_or_mutated) ||
84+
// Give up on loops
85+
tdata.terminator().successors().any(|s| s == bb)
86+
{
87+
return CloneUsage {
88+
cloned_used: true,
89+
clone_consumed_or_mutated: true,
90+
..usage
91+
};
92+
}
93+
94+
let mut v = V {
95+
cloned,
96+
clone,
97+
result: usage,
98+
};
99+
v.visit_basic_block_data(tbb, tdata);
100+
v.result
101+
})
102+
}

0 commit comments

Comments
 (0)