blob: 9f7ec19aa598a072ed7c4e0d57e46898a905f218 [file] [log] [blame]
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{implements_trait, is_copy};
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::sym;
use super::ITER_OVEREAGER_CLONED;
use crate::redundant_clone::REDUNDANT_CLONE;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
cloned_call: &'tcx Expr<'_>,
cloned_recv: &'tcx Expr<'_>,
is_count: bool,
needs_into_iter: bool,
) {
let typeck = cx.typeck_results();
if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator)
&& let Some(method_id) = typeck.type_dependent_def_id(expr.hir_id)
&& cx.tcx.trait_of_item(method_id) == Some(iter_id)
&& let Some(method_id) = typeck.type_dependent_def_id(cloned_call.hir_id)
&& cx.tcx.trait_of_item(method_id) == Some(iter_id)
&& let cloned_recv_ty = typeck.expr_ty_adjusted(cloned_recv)
&& let Some(iter_assoc_ty) = cx.get_associated_type(cloned_recv_ty, iter_id, "Item")
&& matches!(*iter_assoc_ty.kind(), ty::Ref(_, ty, _) if !is_copy(cx, ty))
{
if needs_into_iter
&& let Some(into_iter_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
&& !implements_trait(cx, iter_assoc_ty, into_iter_id, &[])
{
return;
}
let (lint, msg, trailing_clone) = if is_count {
(REDUNDANT_CLONE, "unneeded cloning of iterator items", "")
} else {
(ITER_OVEREAGER_CLONED, "unnecessarily eager cloning of iterator items", ".cloned()")
};
span_lint_and_then(
cx,
lint,
expr.span,
msg,
|diag| {
let method_span = expr.span.with_lo(cloned_call.span.hi());
if let Some(mut snip) = snippet_opt(cx, method_span) {
snip.push_str(trailing_clone);
let replace_span = expr.span.with_lo(cloned_recv.span.hi());
diag.span_suggestion(replace_span, "try", snip, Applicability::MachineApplicable);
}
}
);
}
}