Lint redundant clone of projection

This commit is contained in:
Shotaro Yamada 2018-12-09 20:19:21 +09:00
parent a4fe567602
commit 109d4b1ab3
5 changed files with 106 additions and 23 deletions

View File

@ -57,7 +57,7 @@ fn has_no_effect(cx: &LateContext<'_, '_>, expr: &Expr) -> bool {
}
match expr.node {
ExprKind::Lit(..) | ExprKind::Closure(.., _) => true,
ExprKind::Path(..) => !has_drop(cx, expr),
ExprKind::Path(..) => !has_drop(cx, cx.tables.expr_ty(expr)),
ExprKind::Index(ref a, ref b) | ExprKind::Binary(_, ref a, ref b) => {
has_no_effect(cx, a) && has_no_effect(cx, b)
},
@ -70,7 +70,7 @@ fn has_no_effect(cx: &LateContext<'_, '_>, expr: &Expr) -> bool {
| ExprKind::AddrOf(_, ref inner)
| ExprKind::Box(ref inner) => has_no_effect(cx, inner),
ExprKind::Struct(_, ref fields, ref base) => {
!has_drop(cx, expr)
!has_drop(cx, cx.tables.expr_ty(expr))
&& fields.iter().all(|field| has_no_effect(cx, &field.expr))
&& match *base {
Some(ref base) => has_no_effect(cx, base),
@ -82,7 +82,7 @@ fn has_no_effect(cx: &LateContext<'_, '_>, expr: &Expr) -> bool {
let def = cx.tables.qpath_def(qpath, callee.hir_id);
match def {
Def::Struct(..) | Def::Variant(..) | Def::StructCtor(..) | Def::VariantCtor(..) => {
!has_drop(cx, expr) && args.iter().all(|arg| has_no_effect(cx, arg))
!has_drop(cx, cx.tables.expr_ty(expr)) && args.iter().all(|arg| has_no_effect(cx, arg))
},
_ => false,
}
@ -161,7 +161,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr) -> Option<Vec
| ExprKind::AddrOf(_, ref inner)
| ExprKind::Box(ref inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])),
ExprKind::Struct(_, ref fields, ref base) => {
if has_drop(cx, expr) {
if has_drop(cx, cx.tables.expr_ty(expr)) {
None
} else {
Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect())
@ -172,7 +172,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr) -> Option<Vec
let def = cx.tables.qpath_def(qpath, callee.hir_id);
match def {
Def::Struct(..) | Def::Variant(..) | Def::StructCtor(..) | Def::VariantCtor(..)
if !has_drop(cx, expr) =>
if !has_drop(cx, cx.tables.expr_ty(expr)) =>
{
Some(args.iter().collect())
},

View File

@ -23,10 +23,11 @@ use crate::syntax::{
source_map::{BytePos, Span},
};
use crate::utils::{
in_macro, is_copy, match_def_path, match_type, paths, snippet_opt, span_lint_node, span_lint_node_and_then,
walk_ptrs_ty_depth,
has_drop, in_macro, is_copy, match_def_path, match_type, paths, snippet_opt, span_lint_node,
span_lint_node_and_then, walk_ptrs_ty_depth,
};
use if_chain::if_chain;
use matches::matches;
use std::convert::TryFrom;
macro_rules! unwrap_or_continue {
@ -126,7 +127,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
// _1 in MIR `{ _2 = &_1; clone(move _2); }` or `{ _2 = _1; to_path_buf(_2); } (from_deref)
// In case of `from_deref`, `arg` is already a reference since it is `deref`ed in the previous
// block.
let cloned = unwrap_or_continue!(find_stmt_assigns_to(arg, from_borrow, bbdata.statements.iter().rev()));
let (cloned, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(
cx,
mir,
arg,
from_borrow,
bbdata.statements.iter().rev()
));
if from_borrow && cannot_move_out {
continue;
}
// _1 in MIR `{ _2 = &_1; _3 = deref(move _2); } -> { _4 = _3; to_path_buf(move _4); }`
let referent = if from_deref {
@ -150,7 +161,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
}
};
unwrap_or_continue!(find_stmt_assigns_to(pred_arg, true, mir[ps[0]].statements.iter().rev()))
let (local, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(
cx,
mir,
pred_arg,
true,
mir[ps[0]].statements.iter().rev()
));
if cannot_move_out {
continue;
}
local
} else {
cloned
};
@ -227,21 +248,25 @@ fn is_call_with_ref_arg<'tcx>(
}
}
type CannotMoveOut = bool;
/// Finds the first `to = (&)from`, and returns `Some(from)`.
fn find_stmt_assigns_to<'a, 'tcx: 'a>(
cx: &LateContext<'_, 'tcx>,
mir: &mir::Mir<'tcx>,
to: mir::Local,
by_ref: bool,
mut stmts: impl Iterator<Item = &'a mir::Statement<'tcx>>,
) -> Option<mir::Local> {
) -> Option<(mir::Local, CannotMoveOut)> {
stmts.find_map(|stmt| {
if let mir::StatementKind::Assign(mir::Place::Local(local), v) = &stmt.kind {
if *local == to {
if by_ref {
if let mir::Rvalue::Ref(_, _, mir::Place::Local(r)) = **v {
return Some(r);
if let mir::Rvalue::Ref(_, _, ref place) = **v {
return base_local(cx, mir, place);
}
} else if let mir::Rvalue::Use(mir::Operand::Copy(mir::Place::Local(r))) = **v {
return Some(r);
} else if let mir::Rvalue::Use(mir::Operand::Copy(ref place)) = **v {
return base_local(cx, mir, place);
}
}
}
@ -250,6 +275,32 @@ fn find_stmt_assigns_to<'a, 'tcx: 'a>(
})
}
fn base_local<'tcx>(
cx: &LateContext<'_, 'tcx>,
mir: &mir::Mir<'tcx>,
mut place: &mir::Place<'tcx>,
) -> Option<(mir::Local, CannotMoveOut)> {
use rustc::mir::Place::*;
let mut deref = false;
// Accessing a field of an ADT that has `Drop`
let mut field = false;
loop {
match place {
Local(local) => return Some((*local, deref || field)),
Projection(proj) => {
place = &proj.base;
deref = deref || matches!(proj.elem, mir::ProjectionElem::Deref);
if !field && matches!(proj.elem, mir::ProjectionElem::Field(..)) {
field = has_drop(cx, place.ty(&mir.local_decls, cx.tcx).to_ty(cx.tcx));
}
},
_ => return None,
}
}
}
struct LocalUseVisitor {
local: mir::Local,
used_other_than_drop: bool,
@ -280,7 +331,7 @@ impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
fn visit_local(&mut self, local: &mir::Local, ctx: PlaceContext<'tcx>, _: mir::Location) {
match ctx {
PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) => return,
_ => {}
_ => {},
}
if *local == self.local {

View File

@ -266,9 +266,8 @@ pub fn implements_trait<'a, 'tcx>(
}
/// Check whether this type implements Drop.
pub fn has_drop(cx: &LateContext<'_, '_>, expr: &Expr) -> bool {
let struct_ty = cx.tables.expr_ty(expr);
match struct_ty.ty_adt_def() {
pub fn has_drop<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
match ty.ty_adt_def() {
Some(def) => def.has_dtor(cx.tcx),
_ => false,
}

View File

@ -34,6 +34,12 @@ fn main() {
// Check that lint level works
#[allow(clippy::redundant_clone)] let _ = String::new().to_string();
let tup = (String::from("foo"),);
let _ = tup.0.clone();
let tup_ref = &(String::from("foo"),);
let _s = tup_ref.0.clone(); // this `.clone()` cannot be removed
}
#[derive(Clone)]
@ -45,3 +51,18 @@ fn with_branch(a: Alpha, b: bool) -> (Alpha, Alpha) {
(Alpha, a)
}
}
struct TypeWithDrop {
x: String,
}
impl Drop for TypeWithDrop {
fn drop(&mut self) {}
}
fn cannot_move_from_type_with_drop() -> String {
let s = TypeWithDrop {
x: String::new()
};
s.x.clone() // removing this `clone()` summons E0509
}

View File

@ -96,16 +96,28 @@ note: this value is dropped without further use
| ^^^^^^^^^^^^^^^
error: redundant clone
--> $DIR/redundant_clone.rs:43:22
--> $DIR/redundant_clone.rs:39:18
|
43 | (a.clone(), a.clone())
39 | let _ = tup.0.clone();
| ^^^^^^^^ help: remove this
|
note: this value is dropped without further use
--> $DIR/redundant_clone.rs:39:13
|
39 | let _ = tup.0.clone();
| ^^^^^
error: redundant clone
--> $DIR/redundant_clone.rs:49:22
|
49 | (a.clone(), a.clone())
| ^^^^^^^^ help: remove this
|
note: this value is dropped without further use
--> $DIR/redundant_clone.rs:43:21
--> $DIR/redundant_clone.rs:49:21
|
43 | (a.clone(), a.clone())
49 | (a.clone(), a.clone())
| ^
error: aborting due to 9 previous errors
error: aborting due to 10 previous errors