use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
use clippy_utils::mir::{visit_local_usage, LocalUsage, PossibleBorrowerMap};
use clippy_utils::source::snippet_opt;
-use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth};
+use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, is_type_lang_item, walk_ptrs_ty_depth};
use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths};
-use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
-use rustc_hir::{def_id, Body, FnDecl, HirId};
+use rustc_hir::{def_id, Body, FnDecl, LangItem};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::mir;
use rustc_middle::ty::{self, Ty};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::{BytePos, Span};
-use rustc_span::sym;
+use rustc_session::declare_lint_pass;
+use rustc_span::def_id::LocalDefId;
+use rustc_span::{sym, BytePos, Span};
macro_rules! unwrap_or_continue {
($x:expr) => {
/// False-negatives: analysis performed by this lint is conservative and limited.
///
/// ### Example
- /// ```rust
+ /// ```no_run
/// # use std::path::Path;
/// # #[derive(Clone)]
/// # struct Foo;
/// ```
#[clippy::version = "1.32.0"]
pub REDUNDANT_CLONE,
- perf,
+ nursery,
"`clone()` of an owned value that is going to be dropped immediately"
}
cx: &LateContext<'tcx>,
_: FnKind<'tcx>,
_: &'tcx FnDecl<'_>,
- body: &'tcx Body<'_>,
+ _: &'tcx Body<'_>,
_: Span,
- _: HirId,
+ def_id: LocalDefId,
) {
- let def_id = cx.tcx.hir().body_owner_def_id(body.id());
-
// Building MIR for `fn`s with unsatisfiable preds results in ICE.
if fn_has_unsatisfiable_preds(cx, def_id.to_def_id()) {
return;
unwrap_or_continue!(is_call_with_ref_arg(cx, mir, &terminator.kind));
let from_borrow = match_def_path(cx, fn_def_id, &paths::CLONE_TRAIT_METHOD)
- || match_def_path(cx, fn_def_id, &paths::TO_OWNED_METHOD)
- || (match_def_path(cx, fn_def_id, &paths::TO_STRING_METHOD)
- && is_type_diagnostic_item(cx, arg_ty, sym::String));
+ || cx.tcx.is_diagnostic_item(sym::to_owned_method, fn_def_id)
+ || (cx.tcx.is_diagnostic_item(sym::to_string_method, fn_def_id)
+ && is_type_lang_item(cx, arg_ty, LangItem::String));
let from_deref = !from_borrow
&& (match_def_path(cx, fn_def_id, &paths::PATH_TO_PATH_BUF)
let pred_terminator = mir[ps[0]].terminator();
// receiver of the `deref()` call
- let (pred_arg, deref_clone_ret) = if_chain! {
- if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, res)) =
- is_call_with_ref_arg(cx, mir, &pred_terminator.kind);
- if res == cloned;
- if cx.tcx.is_diagnostic_item(sym::deref_method, pred_fn_def_id);
- if is_type_diagnostic_item(cx, pred_arg_ty, sym::PathBuf)
- || is_type_diagnostic_item(cx, pred_arg_ty, sym::OsString);
- then {
- (pred_arg, res)
- } else {
- continue;
- }
+ let (pred_arg, deref_clone_ret) = if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, res)) =
+ is_call_with_ref_arg(cx, mir, &pred_terminator.kind)
+ && res == cloned
+ && cx.tcx.is_diagnostic_item(sym::deref_method, pred_fn_def_id)
+ && (is_type_diagnostic_item(cx, pred_arg_ty, sym::PathBuf)
+ || is_type_diagnostic_item(cx, pred_arg_ty, sym::OsString))
+ {
+ (pred_arg, res)
+ } else {
+ continue;
};
let (local, cannot_move_out) =
.assert_crate_local()
.lint_root;
- if_chain! {
- if let Some(snip) = snippet_opt(cx, span);
- if let Some(dot) = snip.rfind('.');
- then {
- let sugg_span = span.with_lo(
- span.lo() + BytePos(u32::try_from(dot).unwrap())
- );
- let mut app = Applicability::MaybeIncorrect;
-
- let call_snip = &snip[dot + 1..];
- // Machine applicable when `call_snip` looks like `foobar()`
- if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) {
- if call_snip.as_bytes().iter().all(|b| b.is_ascii_alphabetic() || *b == b'_') {
- app = Applicability::MachineApplicable;
- }
+ if let Some(snip) = snippet_opt(cx, span)
+ && let Some(dot) = snip.rfind('.')
+ {
+ let sugg_span = span.with_lo(span.lo() + BytePos(u32::try_from(dot).unwrap()));
+ let mut app = Applicability::MaybeIncorrect;
+
+ let call_snip = &snip[dot + 1..];
+ // Machine applicable when `call_snip` looks like `foobar()`
+ if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) {
+ if call_snip
+ .as_bytes()
+ .iter()
+ .all(|b| b.is_ascii_alphabetic() || *b == b'_')
+ {
+ app = Applicability::MachineApplicable;
}
+ }
- span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |diag| {
- diag.span_suggestion(
- sugg_span,
- "remove this",
- "",
- app,
+ span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |diag| {
+ diag.span_suggestion(sugg_span, "remove this", "", app);
+ if clone_usage.cloned_used {
+ diag.span_note(span, "cloned value is neither consumed nor mutated");
+ } else {
+ diag.span_note(
+ span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())),
+ "this value is dropped without further use",
);
- if clone_usage.cloned_used {
- diag.span_note(
- span,
- "cloned value is neither consumed nor mutated",
- );
- } else {
- diag.span_note(
- span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())),
- "this value is dropped without further use",
- );
- }
- });
- } else {
- span_lint_hir(cx, REDUNDANT_CLONE, node, span, "redundant clone");
- }
+ }
+ });
+ } else {
+ span_lint_hir(cx, REDUNDANT_CLONE, node, span, "redundant clone");
}
}
}
mir: &'tcx mir::Body<'tcx>,
kind: &'tcx mir::TerminatorKind<'tcx>,
) -> Option<(def_id::DefId, mir::Local, Ty<'tcx>, mir::Local)> {
- if_chain! {
- if let mir::TerminatorKind::Call { func, args, destination, .. } = kind;
- if args.len() == 1;
- if let mir::Operand::Move(mir::Place { local, .. }) = &args[0];
- if let ty::FnDef(def_id, _) = *func.ty(mir, cx.tcx).kind();
- if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(mir, cx.tcx));
- if !is_copy(cx, inner_ty);
- then {
- Some((def_id, *local, inner_ty, destination.as_local()?))
- } else {
- None
- }
+ if let mir::TerminatorKind::Call {
+ func,
+ args,
+ destination,
+ ..
+ } = kind
+ && args.len() == 1
+ && let mir::Operand::Move(mir::Place { local, .. }) = &args[0].node
+ && let ty::FnDef(def_id, _) = *func.ty(mir, cx.tcx).kind()
+ && let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].node.ty(mir, cx.tcx))
+ && !is_copy(cx, inner_ty)
+ {
+ Some((def_id, *local, inner_ty, destination.as_local()?))
+ } else {
+ None
}
}
mir: &mir::Body<'tcx>,
place: mir::Place<'tcx>,
) -> (mir::Local, CannotMoveOut) {
- use rustc_middle::mir::PlaceRef;
-
// Dereference. You cannot move things out from a borrowed value.
let mut deref = false;
// Accessing a field of an ADT that has `Drop`. Moving the field out will cause E0509.
// underlying type implements Copy
let mut slice = false;
- let PlaceRef { local, mut projection } = place.as_ref();
- while let [base @ .., elem] = projection {
- projection = base;
+ for (base, elem) in place.as_ref().iter_projections() {
+ let base_ty = base.ty(&mir.local_decls, cx.tcx).ty;
deref |= matches!(elem, mir::ProjectionElem::Deref);
- field |= matches!(elem, mir::ProjectionElem::Field(..))
- && has_drop(cx, mir::Place::ty_from(local, projection, &mir.local_decls, cx.tcx).ty);
- slice |= matches!(elem, mir::ProjectionElem::Index(..))
- && !is_copy(cx, mir::Place::ty_from(local, projection, &mir.local_decls, cx.tcx).ty);
+ field |= matches!(elem, mir::ProjectionElem::Field(..)) && has_drop(cx, base_ty);
+ slice |= matches!(elem, mir::ProjectionElem::Index(..)) && !is_copy(cx, base_ty);
}
- (local, deref || field || slice)
+ (place.local, deref || field || slice)
}
#[derive(Default)]