Skip to content

Commit 6d6ff88

Browse files
committed
Refactor
1 parent 9a150b4 commit 6d6ff88

File tree

1 file changed

+67
-79
lines changed

1 file changed

+67
-79
lines changed

clippy_lints/src/redundant_clone.rs

Lines changed: 67 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ use crate::utils::{
2929
use if_chain::if_chain;
3030
use std::convert::TryFrom;
3131

32+
macro_rules! unwrap_or_continue {
33+
($x:expr) => {
34+
match $x {
35+
Some(x) => x,
36+
None => continue,
37+
}
38+
};
39+
}
40+
3241
/// **What it does:** Checks for a redudant `clone()` (and its relatives) which clones an owned
3342
/// value that is going to be dropped without further use.
3443
///
@@ -87,40 +96,15 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
8796
let def_id = cx.tcx.hir.body_owner_def_id(body.id());
8897
let mir = cx.tcx.optimized_mir(def_id);
8998

90-
// Looks for `call(x: &T)` where `T: !Copy`
91-
let call = |kind: &mir::TerminatorKind<'tcx>| -> Option<(def_id::DefId, mir::Local, ty::Ty<'tcx>)> {
92-
if_chain! {
93-
if let TerminatorKind::Call { func, args, .. } = kind;
94-
if args.len() == 1;
95-
if let mir::Operand::Move(mir::Place::Local(local)) = &args[0];
96-
if let ty::FnDef(def_id, _) = func.ty(&*mir, cx.tcx).sty;
97-
if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx));
98-
if !is_copy(cx, inner_ty);
99-
then {
100-
Some((def_id, *local, inner_ty))
101-
} else {
102-
None
103-
}
104-
}
105-
};
106-
10799
for (bb, bbdata) in mir.basic_blocks().iter_enumerated() {
108-
let terminator = if let Some(terminator) = &bbdata.terminator {
109-
terminator
110-
} else {
111-
continue;
112-
};
100+
let terminator = unwrap_or_continue!(&bbdata.terminator);
113101

114102
// Give up on loops
115103
if terminator.successors().any(|s| *s == bb) {
116104
continue;
117105
}
118106

119-
let (fn_def_id, arg, arg_ty) = if let Some(t) = call(&terminator.kind) {
120-
t
121-
} else {
122-
continue;
123-
};
107+
let (fn_def_id, arg, arg_ty, _) = unwrap_or_continue!(is_call_with_ref_arg(cx, mir, &terminator.kind));
124108

125109
let from_borrow = match_def_path(cx.tcx, fn_def_id, &paths::CLONE_TRAIT_METHOD)
126110
|| match_def_path(cx.tcx, fn_def_id, &paths::TO_OWNED_METHOD)
@@ -135,43 +119,23 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
135119
continue;
136120
}
137121

138-
// _1 in MIR `{ _2 = &_1; clone(move _2); }` or `{ _2 = _1; to_path_buf(_2); }
139-
let cloned = if let Some(referent) = bbdata
140-
.statements
141-
.iter()
142-
.rev()
143-
.filter_map(|stmt| {
144-
if let mir::StatementKind::Assign(mir::Place::Local(local), v) = &stmt.kind {
145-
if *local == arg {
146-
if from_deref {
147-
// `r` is already a reference.
148-
if let mir::Rvalue::Use(mir::Operand::Copy(mir::Place::Local(r))) = **v {
149-
return Some(r);
150-
}
151-
} else if let mir::Rvalue::Ref(_, _, mir::Place::Local(r)) = **v {
152-
return Some(r);
153-
}
154-
}
155-
}
156-
157-
None
158-
})
159-
.next()
160-
{
161-
referent
162-
} else {
163-
continue;
164-
};
122+
// _1 in MIR `{ _2 = &_1; clone(move _2); }` or `{ _2 = _1; to_path_buf(_2); } (from_deref)
123+
// In case of `from_deref`, `arg` is already a reference since it is `deref`ed in the previous
124+
// block.
125+
let cloned = unwrap_or_continue!(find_stmt_assigns_to(arg, from_borrow, bbdata.statements.iter().rev()));
165126

166127
// _1 in MIR `{ _2 = &_1; _3 = deref(move _2); } -> { _4 = _3; to_path_buf(move _4); }`
167128
let referent = if from_deref {
168129
let ps = mir.predecessors_for(bb);
130+
if ps.len() != 1 {
131+
continue;
132+
}
133+
let pred_terminator = unwrap_or_continue!(&mir[ps[0]].terminator);
134+
169135
let pred_arg = if_chain! {
170-
if ps.len() == 1;
171-
if let Some(pred_terminator) = &mir[ps[0]].terminator;
172-
if let mir::TerminatorKind::Call { destination: Some((res, _)), .. } = &pred_terminator.kind;
136+
if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, Some(res))) =
137+
is_call_with_ref_arg(cx, mir, &pred_terminator.kind);
173138
if *res == mir::Place::Local(cloned);
174-
if let Some((pred_fn_def_id, pred_arg, pred_arg_ty)) = call(&pred_terminator.kind);
175139
if match_def_path(cx.tcx, pred_fn_def_id, &paths::DEREF_TRAIT_METHOD);
176140
if match_type(cx, pred_arg_ty, &paths::PATH_BUF)
177141
|| match_type(cx, pred_arg_ty, &paths::OS_STRING);
@@ -182,27 +146,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
182146
}
183147
};
184148

185-
if let Some(referent) = mir[ps[0]]
186-
.statements
187-
.iter()
188-
.rev()
189-
.filter_map(|stmt| {
190-
if let mir::StatementKind::Assign(mir::Place::Local(l), v) = &stmt.kind {
191-
if *l == pred_arg {
192-
if let mir::Rvalue::Ref(_, _, mir::Place::Local(referent)) = **v {
193-
return Some(referent);
194-
}
195-
}
196-
}
197-
198-
None
199-
})
200-
.next()
201-
{
202-
referent
203-
} else {
204-
continue;
205-
}
149+
unwrap_or_continue!(find_stmt_assigns_to(pred_arg, true, mir[ps[0]].statements.iter().rev()))
206150
} else {
207151
cloned
208152
};
@@ -261,6 +205,50 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
261205
}
262206
}
263207

208+
/// If `kind` is `y = func(x: &T)` where `T: !Copy`, returns `(DefId of func, x, T, y)`.
209+
fn is_call_with_ref_arg<'tcx>(
210+
cx: &LateContext<'_, 'tcx>,
211+
mir: &'tcx mir::Mir<'tcx>,
212+
kind: &'tcx mir::TerminatorKind<'tcx>,
213+
) -> Option<(def_id::DefId, mir::Local, ty::Ty<'tcx>, Option<&'tcx mir::Place<'tcx>>)> {
214+
if_chain! {
215+
if let TerminatorKind::Call { func, args, destination, .. } = kind;
216+
if args.len() == 1;
217+
if let mir::Operand::Move(mir::Place::Local(local)) = &args[0];
218+
if let ty::FnDef(def_id, _) = func.ty(&*mir, cx.tcx).sty;
219+
if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx));
220+
if !is_copy(cx, inner_ty);
221+
then {
222+
Some((def_id, *local, inner_ty, destination.as_ref().map(|(dest, _)| dest)))
223+
} else {
224+
None
225+
}
226+
}
227+
}
228+
229+
/// Finds the first `to = (&)from`, and returns `Some(from)`.
230+
fn find_stmt_assigns_to<'a, 'tcx: 'a>(
231+
to: mir::Local,
232+
by_ref: bool,
233+
mut stmts: impl Iterator<Item = &'a mir::Statement<'tcx>>,
234+
) -> Option<mir::Local> {
235+
stmts.find_map(|stmt| {
236+
if let mir::StatementKind::Assign(mir::Place::Local(local), v) = &stmt.kind {
237+
if *local == to {
238+
if by_ref {
239+
if let mir::Rvalue::Ref(_, _, mir::Place::Local(r)) = **v {
240+
return Some(r);
241+
}
242+
} else if let mir::Rvalue::Use(mir::Operand::Copy(mir::Place::Local(r))) = **v {
243+
return Some(r);
244+
}
245+
}
246+
}
247+
248+
None
249+
})
250+
}
251+
264252
struct LocalUseVisitor {
265253
local: mir::Local,
266254
used_other_than_drop: bool,

0 commit comments

Comments
 (0)