add lint manual_next_back
checks for manual reverse iteration (`.rev().next()`) of a `DoubleEndedIterator`
This commit is contained in:
parent
c56dd3d4c0
commit
a8834bc46a
@ -4785,6 +4785,7 @@ Released 2018-09-13
|
||||
[`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str
|
||||
[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
|
||||
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
|
||||
[`manual_next_back`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_next_back
|
||||
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
|
||||
[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
|
||||
[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
|
||||
|
@ -351,6 +351,7 @@
|
||||
crate::methods::ITER_WITH_DRAIN_INFO,
|
||||
crate::methods::MANUAL_FILTER_MAP_INFO,
|
||||
crate::methods::MANUAL_FIND_MAP_INFO,
|
||||
crate::methods::MANUAL_NEXT_BACK_INFO,
|
||||
crate::methods::MANUAL_OK_OR_INFO,
|
||||
crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO,
|
||||
crate::methods::MANUAL_SPLIT_ONCE_INFO,
|
||||
|
38
clippy_lints/src/methods/manual_next_back.rs
Normal file
38
clippy_lints/src/methods/manual_next_back.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
rev_call: &'tcx Expr<'_>,
|
||||
rev_recv: &'tcx Expr<'_>,
|
||||
) {
|
||||
let rev_recv_ty = cx.typeck_results().expr_ty(rev_recv);
|
||||
|
||||
// check that the receiver of `rev` implements `DoubleEndedIterator` and
|
||||
// that `rev` and `next` come from `Iterator`
|
||||
if cx
|
||||
.tcx
|
||||
.get_diagnostic_item(sym::DoubleEndedIterator)
|
||||
.map_or(false, |double_ended_iterator| {
|
||||
implements_trait(cx, rev_recv_ty, double_ended_iterator, &[])
|
||||
})
|
||||
&& is_trait_method(cx, rev_call, sym::Iterator)
|
||||
&& is_trait_method(cx, expr, sym::Iterator)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
super::MANUAL_NEXT_BACK,
|
||||
expr.span.with_lo(rev_recv.span.hi()),
|
||||
"manual backwards iteration",
|
||||
"use",
|
||||
String::from(".next_back()"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
@ -45,6 +45,7 @@
|
||||
mod iter_skip_next;
|
||||
mod iter_with_drain;
|
||||
mod iterator_step_by_zero;
|
||||
mod manual_next_back;
|
||||
mod manual_ok_or;
|
||||
mod manual_saturating_arithmetic;
|
||||
mod manual_str_repeat;
|
||||
@ -3193,6 +3194,29 @@
|
||||
"calling `drain` in order to `clear` a container"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `.rev().next()` on a `DoubleEndedIterator`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `.next_back()` is cleaner.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let foo = [0; 10];
|
||||
/// foo.iter().rev().next();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let foo = [0; 10];
|
||||
/// foo.iter().next_back();
|
||||
/// ```
|
||||
#[clippy::version = "1.71.0"]
|
||||
pub MANUAL_NEXT_BACK,
|
||||
style,
|
||||
"manual reverse iteration of `DoubleEndedIterator`"
|
||||
}
|
||||
|
||||
pub struct Methods {
|
||||
avoid_breaking_exported_api: bool,
|
||||
msrv: Msrv,
|
||||
@ -3321,6 +3345,7 @@ pub fn new(
|
||||
NEEDLESS_COLLECT,
|
||||
SUSPICIOUS_COMMAND_ARG_SPACE,
|
||||
CLEAR_WITH_DRAIN,
|
||||
MANUAL_NEXT_BACK,
|
||||
]);
|
||||
|
||||
/// Extracts a method call name, args, and `Span` of the method name.
|
||||
@ -3677,6 +3702,7 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
("iter", []) => iter_next_slice::check(cx, expr, recv2),
|
||||
("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg),
|
||||
("skip_while", [_]) => skip_while_next::check(cx, expr),
|
||||
("rev", [])=> manual_next_back::check(cx, expr, recv, recv2),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
36
tests/ui/manual_next_back.fixed
Normal file
36
tests/ui/manual_next_back.fixed
Normal file
@ -0,0 +1,36 @@
|
||||
//@run-rustfix
|
||||
|
||||
#![allow(unused)]
|
||||
#![warn(clippy::manual_next_back)]
|
||||
|
||||
struct FakeIter(std::ops::Range<i32>);
|
||||
|
||||
impl FakeIter {
|
||||
fn rev(self) -> Self {
|
||||
self
|
||||
}
|
||||
|
||||
fn next(&self) {}
|
||||
}
|
||||
|
||||
impl DoubleEndedIterator for FakeIter {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.0.next_back()
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for FakeIter {
|
||||
type Item = i32;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.0.next()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// should not lint
|
||||
FakeIter(0..10).rev().next();
|
||||
|
||||
// should lint
|
||||
let _ = (0..10).next_back().unwrap();
|
||||
let _ = "something".bytes().next_back();
|
||||
}
|
36
tests/ui/manual_next_back.rs
Normal file
36
tests/ui/manual_next_back.rs
Normal file
@ -0,0 +1,36 @@
|
||||
//@run-rustfix
|
||||
|
||||
#![allow(unused)]
|
||||
#![warn(clippy::manual_next_back)]
|
||||
|
||||
struct FakeIter(std::ops::Range<i32>);
|
||||
|
||||
impl FakeIter {
|
||||
fn rev(self) -> Self {
|
||||
self
|
||||
}
|
||||
|
||||
fn next(&self) {}
|
||||
}
|
||||
|
||||
impl DoubleEndedIterator for FakeIter {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.0.next_back()
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for FakeIter {
|
||||
type Item = i32;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.0.next()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// should not lint
|
||||
FakeIter(0..10).rev().next();
|
||||
|
||||
// should lint
|
||||
let _ = (0..10).rev().next().unwrap();
|
||||
let _ = "something".bytes().rev().next();
|
||||
}
|
16
tests/ui/manual_next_back.stderr
Normal file
16
tests/ui/manual_next_back.stderr
Normal file
@ -0,0 +1,16 @@
|
||||
error: manual backwards iteration
|
||||
--> $DIR/manual_next_back.rs:34:20
|
||||
|
|
||||
LL | let _ = (0..10).rev().next().unwrap();
|
||||
| ^^^^^^^^^^^^^ help: use: `.next_back()`
|
||||
|
|
||||
= note: `-D clippy::manual-next-back` implied by `-D warnings`
|
||||
|
||||
error: manual backwards iteration
|
||||
--> $DIR/manual_next_back.rs:35:32
|
||||
|
|
||||
LL | let _ = "something".bytes().rev().next();
|
||||
| ^^^^^^^^^^^^^ help: use: `.next_back()`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
Loading…
Reference in New Issue
Block a user