Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit d71dea4

Browse files
committed
Auto merge of rust-lang#6577 - nahuakang:inspect_then_for_each, r=flip1995
New Lint: inspect_then_for_each **Work In Progress** This PR addresses [Issue 5209](rust-lang/rust-clippy#5209) and adds a new lint called `inspect_then_for_each`. Current seek some guidance. If you added a new lint, here's a checklist for things that will be checked during review or continuous integration. - \[x] Followed [lint naming conventions][lint_naming] - \[x] Added passing UI tests (including committed `.stderr` file) - \[x] `cargo test` passes locally - \[x] Executed `cargo dev update_lints` - \[x] Added lint documentation - \[x] Run `cargo dev fmt` [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints --- changelog: Add [`inspect_for_each`] lint for the use of `inspect().for_each()` on `Iterators`.
2 parents e21270b + 3269070 commit d71dea4

File tree

6 files changed

+98
-0
lines changed

6 files changed

+98
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1997,6 +1997,7 @@ Released 2018-09-13
19971997
[`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax
19981998
[`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax
19991999
[`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body
2000+
[`inspect_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#inspect_for_each
20002001
[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
20012002
[`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
20022003
[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division

clippy_lints/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
736736
&methods::FROM_ITER_INSTEAD_OF_COLLECT,
737737
&methods::GET_UNWRAP,
738738
&methods::INEFFICIENT_TO_STRING,
739+
&methods::INSPECT_FOR_EACH,
739740
&methods::INTO_ITER_ON_REF,
740741
&methods::ITERATOR_STEP_BY_ZERO,
741742
&methods::ITER_CLONED_COLLECT,
@@ -1514,6 +1515,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
15141515
LintId::of(&methods::FILTER_NEXT),
15151516
LintId::of(&methods::FLAT_MAP_IDENTITY),
15161517
LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT),
1518+
LintId::of(&methods::INSPECT_FOR_EACH),
15171519
LintId::of(&methods::INTO_ITER_ON_REF),
15181520
LintId::of(&methods::ITERATOR_STEP_BY_ZERO),
15191521
LintId::of(&methods::ITER_CLONED_COLLECT),
@@ -1815,6 +1817,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
18151817
LintId::of(&methods::CLONE_ON_COPY),
18161818
LintId::of(&methods::FILTER_NEXT),
18171819
LintId::of(&methods::FLAT_MAP_IDENTITY),
1820+
LintId::of(&methods::INSPECT_FOR_EACH),
18181821
LintId::of(&methods::OPTION_AS_REF_DEREF),
18191822
LintId::of(&methods::SEARCH_IS_SOME),
18201823
LintId::of(&methods::SKIP_WHILE_NEXT),
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use rustc_hir as hir;
2+
use rustc_lint::LateContext;
3+
use rustc_span::source_map::Span;
4+
5+
use crate::utils::{match_trait_method, paths, span_lint_and_help};
6+
7+
use super::INSPECT_FOR_EACH;
8+
9+
/// lint use of `inspect().for_each()` for `Iterators`
10+
pub(super) fn lint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, inspect_span: Span) {
11+
if match_trait_method(cx, expr, &paths::ITERATOR) {
12+
let msg = "called `inspect(..).for_each(..)` on an `Iterator`";
13+
let hint = "move the code from `inspect(..)` to `for_each(..)` and remove the `inspect(..)`";
14+
span_lint_and_help(
15+
cx,
16+
INSPECT_FOR_EACH,
17+
inspect_span.with_hi(expr.span.hi()),
18+
msg,
19+
None,
20+
hint,
21+
);
22+
}
23+
}

clippy_lints/src/methods/mod.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod bind_instead_of_map;
22
mod inefficient_to_string;
3+
mod inspect_for_each;
34
mod manual_saturating_arithmetic;
45
mod option_map_unwrap_or;
56
mod unnecessary_filter_map;
@@ -1405,6 +1406,36 @@ declare_clippy_lint! {
14051406
"use `.collect()` instead of `::from_iter()`"
14061407
}
14071408

1409+
declare_clippy_lint! {
1410+
/// **What it does:** Checks for usage of `inspect().for_each()`.
1411+
///
1412+
/// **Why is this bad?** It is the same as performing the computation
1413+
/// inside `inspect` at the beginning of the closure in `for_each`.
1414+
///
1415+
/// **Known problems:** None.
1416+
///
1417+
/// **Example:**
1418+
///
1419+
/// ```rust
1420+
/// [1,2,3,4,5].iter()
1421+
/// .inspect(|&x| println!("inspect the number: {}", x))
1422+
/// .for_each(|&x| {
1423+
/// assert!(x >= 0);
1424+
/// });
1425+
/// ```
1426+
/// Can be written as
1427+
/// ```rust
1428+
/// [1,2,3,4,5].iter()
1429+
/// .for_each(|&x| {
1430+
/// println!("inspect the number: {}", x);
1431+
/// assert!(x >= 0);
1432+
/// });
1433+
/// ```
1434+
pub INSPECT_FOR_EACH,
1435+
complexity,
1436+
"using `.inspect().for_each()`, which can be replaced with `.for_each()`"
1437+
}
1438+
14081439
pub struct Methods {
14091440
msrv: Option<RustcVersion>,
14101441
}
@@ -1467,6 +1498,7 @@ impl_lint_pass!(Methods => [
14671498
UNNECESSARY_LAZY_EVALUATIONS,
14681499
MAP_COLLECT_RESULT_UNIT,
14691500
FROM_ITER_INSTEAD_OF_COLLECT,
1501+
INSPECT_FOR_EACH,
14701502
]);
14711503

14721504
impl<'tcx> LateLintPass<'tcx> for Methods {
@@ -1553,6 +1585,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
15531585
["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"),
15541586
["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"),
15551587
["collect", "map"] => lint_map_collect(cx, expr, arg_lists[1], arg_lists[0]),
1588+
["for_each", "inspect"] => inspect_for_each::lint(cx, expr, method_spans[1]),
15561589
_ => {},
15571590
}
15581591

tests/ui/inspect_for_each.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#![warn(clippy::inspect_for_each)]
2+
3+
fn main() {
4+
let a: Vec<usize> = vec![1, 2, 3, 4, 5];
5+
6+
let mut b: Vec<usize> = Vec::new();
7+
a.into_iter().inspect(|x| assert!(*x > 0)).for_each(|x| {
8+
let y = do_some(x);
9+
let z = do_more(y);
10+
b.push(z);
11+
});
12+
13+
assert_eq!(b, vec![4, 5, 6, 7, 8]);
14+
}
15+
16+
fn do_some(a: usize) -> usize {
17+
a + 1
18+
}
19+
20+
fn do_more(a: usize) -> usize {
21+
a + 2
22+
}

tests/ui/inspect_for_each.stderr

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: called `inspect(..).for_each(..)` on an `Iterator`
2+
--> $DIR/inspect_for_each.rs:7:19
3+
|
4+
LL | a.into_iter().inspect(|x| assert!(*x > 0)).for_each(|x| {
5+
| ___________________^
6+
LL | | let y = do_some(x);
7+
LL | | let z = do_more(y);
8+
LL | | b.push(z);
9+
LL | | });
10+
| |______^
11+
|
12+
= note: `-D clippy::inspect-for-each` implied by `-D warnings`
13+
= help: move the code from `inspect(..)` to `for_each(..)` and remove the `inspect(..)`
14+
15+
error: aborting due to previous error
16+

0 commit comments

Comments
 (0)