.last() to .next_back() requires a mutable receiver
In the case where `iter` is a `DoubleEndedIterator`, replacing a call to `iter.last()` (which consumes `iter`) by `iter.next_back()` (which requires a mutable reference to `iter`) cannot be done when `iter` Is not a mutable binding or a mutable reference. When `iter` is a local binding, it can be made mutable by fixing its definition site.
This commit is contained in:
parent
510d3b69fc
commit
45f7a60d31
6 changed files with 169 additions and 21 deletions
|
|
@ -1,8 +1,8 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{is_mutable, is_trait_method, path_to_local};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_hir::{Expr, Node, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::Instance;
|
||||
use rustc_span::{Span, sym};
|
||||
|
|
@ -28,14 +28,40 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, self_expr: &'_ Exp
|
|||
// if the resolved method is the same as the provided definition
|
||||
&& fn_def.def_id() == last_def.def_id
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
let mut sugg = vec![(call_span, String::from("next_back()"))];
|
||||
let mut dont_apply = false;
|
||||
// if `self_expr` is a reference, it is mutable because it is used for `.last()`
|
||||
if !(is_mutable(cx, self_expr) || self_type.is_ref()) {
|
||||
if let Some(hir_id) = path_to_local(self_expr)
|
||||
&& let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
|
||||
&& let PatKind::Binding(_, _, ident, _) = pat.kind
|
||||
{
|
||||
sugg.push((ident.span.shrink_to_lo(), String::from("mut ")));
|
||||
} else {
|
||||
// If we can't make the binding mutable, make the suggestion `Unspecified` to prevent it from being
|
||||
// automatically applied, and add a complementary help message.
|
||||
dont_apply = true;
|
||||
}
|
||||
}
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
DOUBLE_ENDED_ITERATOR_LAST,
|
||||
call_span,
|
||||
expr.span,
|
||||
"called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator",
|
||||
"try",
|
||||
"next_back()".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
|diag| {
|
||||
diag.multipart_suggestion(
|
||||
"try",
|
||||
sugg,
|
||||
if dont_apply {
|
||||
Applicability::Unspecified
|
||||
} else {
|
||||
Applicability::MachineApplicable
|
||||
},
|
||||
);
|
||||
if dont_apply {
|
||||
diag.span_note(self_expr.span, "this must be made mutable to use `.next_back()`");
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue