Skip to content

Commit 3d456ce

Browse files
committed
Auto merge of rust-lang#10769 - Icxolu:manual_next_back, r=giraffate
add lint `manual_next_back` changelog: [`manual_next_back`]: checks for manual reverse iteration (`.rev().next()`) of a `DoubleEndedIterator` fixes rust-lang#10274
2 parents c56dd3d + a8834bc commit 3d456ce

File tree

7 files changed

+154
-0
lines changed

7 files changed

+154
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4785,6 +4785,7 @@ Released 2018-09-13
47854785
[`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str
47864786
[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
47874787
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
4788+
[`manual_next_back`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_next_back
47884789
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
47894790
[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
47904791
[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
351351
crate::methods::ITER_WITH_DRAIN_INFO,
352352
crate::methods::MANUAL_FILTER_MAP_INFO,
353353
crate::methods::MANUAL_FIND_MAP_INFO,
354+
crate::methods::MANUAL_NEXT_BACK_INFO,
354355
crate::methods::MANUAL_OK_OR_INFO,
355356
crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO,
356357
crate::methods::MANUAL_SPLIT_ONCE_INFO,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::is_trait_method;
3+
use clippy_utils::ty::implements_trait;
4+
use rustc_errors::Applicability;
5+
use rustc_hir::Expr;
6+
use rustc_lint::LateContext;
7+
use rustc_span::symbol::sym;
8+
9+
pub(super) fn check<'tcx>(
10+
cx: &LateContext<'tcx>,
11+
expr: &'tcx Expr<'_>,
12+
rev_call: &'tcx Expr<'_>,
13+
rev_recv: &'tcx Expr<'_>,
14+
) {
15+
let rev_recv_ty = cx.typeck_results().expr_ty(rev_recv);
16+
17+
// check that the receiver of `rev` implements `DoubleEndedIterator` and
18+
// that `rev` and `next` come from `Iterator`
19+
if cx
20+
.tcx
21+
.get_diagnostic_item(sym::DoubleEndedIterator)
22+
.map_or(false, |double_ended_iterator| {
23+
implements_trait(cx, rev_recv_ty, double_ended_iterator, &[])
24+
})
25+
&& is_trait_method(cx, rev_call, sym::Iterator)
26+
&& is_trait_method(cx, expr, sym::Iterator)
27+
{
28+
span_lint_and_sugg(
29+
cx,
30+
super::MANUAL_NEXT_BACK,
31+
expr.span.with_lo(rev_recv.span.hi()),
32+
"manual backwards iteration",
33+
"use",
34+
String::from(".next_back()"),
35+
Applicability::MachineApplicable,
36+
);
37+
}
38+
}

clippy_lints/src/methods/mod.rs

+26
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ mod iter_overeager_cloned;
4545
mod iter_skip_next;
4646
mod iter_with_drain;
4747
mod iterator_step_by_zero;
48+
mod manual_next_back;
4849
mod manual_ok_or;
4950
mod manual_saturating_arithmetic;
5051
mod manual_str_repeat;
@@ -3193,6 +3194,29 @@ declare_clippy_lint! {
31933194
"calling `drain` in order to `clear` a container"
31943195
}
31953196

3197+
declare_clippy_lint! {
3198+
/// ### What it does
3199+
/// Checks for `.rev().next()` on a `DoubleEndedIterator`
3200+
///
3201+
/// ### Why is this bad?
3202+
/// `.next_back()` is cleaner.
3203+
///
3204+
/// ### Example
3205+
/// ```rust
3206+
/// # let foo = [0; 10];
3207+
/// foo.iter().rev().next();
3208+
/// ```
3209+
/// Use instead:
3210+
/// ```rust
3211+
/// # let foo = [0; 10];
3212+
/// foo.iter().next_back();
3213+
/// ```
3214+
#[clippy::version = "1.71.0"]
3215+
pub MANUAL_NEXT_BACK,
3216+
style,
3217+
"manual reverse iteration of `DoubleEndedIterator`"
3218+
}
3219+
31963220
pub struct Methods {
31973221
avoid_breaking_exported_api: bool,
31983222
msrv: Msrv,
@@ -3321,6 +3345,7 @@ impl_lint_pass!(Methods => [
33213345
NEEDLESS_COLLECT,
33223346
SUSPICIOUS_COMMAND_ARG_SPACE,
33233347
CLEAR_WITH_DRAIN,
3348+
MANUAL_NEXT_BACK,
33243349
]);
33253350

33263351
/// Extracts a method call name, args, and `Span` of the method name.
@@ -3677,6 +3702,7 @@ impl Methods {
36773702
("iter", []) => iter_next_slice::check(cx, expr, recv2),
36783703
("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg),
36793704
("skip_while", [_]) => skip_while_next::check(cx, expr),
3705+
("rev", [])=> manual_next_back::check(cx, expr, recv, recv2),
36803706
_ => {},
36813707
}
36823708
}

tests/ui/manual_next_back.fixed

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//@run-rustfix
2+
3+
#![allow(unused)]
4+
#![warn(clippy::manual_next_back)]
5+
6+
struct FakeIter(std::ops::Range<i32>);
7+
8+
impl FakeIter {
9+
fn rev(self) -> Self {
10+
self
11+
}
12+
13+
fn next(&self) {}
14+
}
15+
16+
impl DoubleEndedIterator for FakeIter {
17+
fn next_back(&mut self) -> Option<Self::Item> {
18+
self.0.next_back()
19+
}
20+
}
21+
22+
impl Iterator for FakeIter {
23+
type Item = i32;
24+
fn next(&mut self) -> Option<Self::Item> {
25+
self.0.next()
26+
}
27+
}
28+
29+
fn main() {
30+
// should not lint
31+
FakeIter(0..10).rev().next();
32+
33+
// should lint
34+
let _ = (0..10).next_back().unwrap();
35+
let _ = "something".bytes().next_back();
36+
}

tests/ui/manual_next_back.rs

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//@run-rustfix
2+
3+
#![allow(unused)]
4+
#![warn(clippy::manual_next_back)]
5+
6+
struct FakeIter(std::ops::Range<i32>);
7+
8+
impl FakeIter {
9+
fn rev(self) -> Self {
10+
self
11+
}
12+
13+
fn next(&self) {}
14+
}
15+
16+
impl DoubleEndedIterator for FakeIter {
17+
fn next_back(&mut self) -> Option<Self::Item> {
18+
self.0.next_back()
19+
}
20+
}
21+
22+
impl Iterator for FakeIter {
23+
type Item = i32;
24+
fn next(&mut self) -> Option<Self::Item> {
25+
self.0.next()
26+
}
27+
}
28+
29+
fn main() {
30+
// should not lint
31+
FakeIter(0..10).rev().next();
32+
33+
// should lint
34+
let _ = (0..10).rev().next().unwrap();
35+
let _ = "something".bytes().rev().next();
36+
}

tests/ui/manual_next_back.stderr

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: manual backwards iteration
2+
--> $DIR/manual_next_back.rs:34:20
3+
|
4+
LL | let _ = (0..10).rev().next().unwrap();
5+
| ^^^^^^^^^^^^^ help: use: `.next_back()`
6+
|
7+
= note: `-D clippy::manual-next-back` implied by `-D warnings`
8+
9+
error: manual backwards iteration
10+
--> $DIR/manual_next_back.rs:35:32
11+
|
12+
LL | let _ = "something".bytes().rev().next();
13+
| ^^^^^^^^^^^^^ help: use: `.next_back()`
14+
15+
error: aborting due to 2 previous errors
16+

0 commit comments

Comments
 (0)