Skip to content

Commit ad9ceee

Browse files
committed
Auto merge of #6685 - magurotuna:filter_map_identity, r=phansch
Add new lint `filter_map_identity` <!-- Thank you for making Clippy better! We're collecting our changelog from pull request descriptions. If your PR only includes internal changes, you can just write `changelog: none`. Otherwise, please write a short comment explaining your change. If your PR fixes an issue, you can add "fixes #issue_number" into this PR description. This way the issue will be automatically closed when your PR is merged. 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 Note that you can skip the above if you are just opening a WIP PR in order to get feedback. Delete this line and everything above before opening your PR. --> This commit adds a new lint named filter_map_identity. This lint is the same as `flat_map_identity` except that it checks for the usage of `filter_map`. --- Closes #6643 changelog: Added a new lint: `filter_map_identity`
2 parents 001185d + fbe436b commit ad9ceee

File tree

8 files changed

+144
-2
lines changed

8 files changed

+144
-2
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1955,6 +1955,7 @@ Released 2018-09-13
19551955
[`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default
19561956
[`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file
19571957
[`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map
1958+
[`filter_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_identity
19581959
[`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next
19591960
[`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next
19601961
[`find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#find_map

clippy_lints/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
743743
&methods::EXPECT_USED,
744744
&methods::FILETYPE_IS_FILE,
745745
&methods::FILTER_MAP,
746+
&methods::FILTER_MAP_IDENTITY,
746747
&methods::FILTER_MAP_NEXT,
747748
&methods::FILTER_NEXT,
748749
&methods::FLAT_MAP_IDENTITY,
@@ -1535,6 +1536,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
15351536
LintId::of(&methods::CLONE_DOUBLE_REF),
15361537
LintId::of(&methods::CLONE_ON_COPY),
15371538
LintId::of(&methods::EXPECT_FUN_CALL),
1539+
LintId::of(&methods::FILTER_MAP_IDENTITY),
15381540
LintId::of(&methods::FILTER_NEXT),
15391541
LintId::of(&methods::FLAT_MAP_IDENTITY),
15401542
LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT),
@@ -1842,6 +1844,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
18421844
LintId::of(&matches::WILDCARD_IN_OR_PATTERNS),
18431845
LintId::of(&methods::BIND_INSTEAD_OF_MAP),
18441846
LintId::of(&methods::CLONE_ON_COPY),
1847+
LintId::of(&methods::FILTER_MAP_IDENTITY),
18451848
LintId::of(&methods::FILTER_NEXT),
18461849
LintId::of(&methods::FLAT_MAP_IDENTITY),
18471850
LintId::of(&methods::INSPECT_FOR_EACH),

clippy_lints/src/loops.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult
738738
fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult {
739739
let stmts = block.stmts.iter().map(stmt_to_expr);
740740
let expr = once(block.expr.as_deref());
741-
let mut iter = stmts.chain(expr).filter_map(|e| e);
741+
let mut iter = stmts.chain(expr).flatten();
742742
never_loop_expr_seq(&mut iter, main_loop_id)
743743
}
744744

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use crate::utils::{match_qpath, match_trait_method, paths, span_lint_and_sugg};
2+
use if_chain::if_chain;
3+
use rustc_errors::Applicability;
4+
use rustc_hir as hir;
5+
use rustc_lint::LateContext;
6+
use rustc_span::source_map::Span;
7+
8+
use super::FILTER_MAP_IDENTITY;
9+
10+
pub(super) fn check(
11+
cx: &LateContext<'_>,
12+
expr: &hir::Expr<'_>,
13+
filter_map_args: &[hir::Expr<'_>],
14+
filter_map_span: Span,
15+
) {
16+
if match_trait_method(cx, expr, &paths::ITERATOR) {
17+
let arg_node = &filter_map_args[1].kind;
18+
19+
let apply_lint = |message: &str| {
20+
span_lint_and_sugg(
21+
cx,
22+
FILTER_MAP_IDENTITY,
23+
filter_map_span.with_hi(expr.span.hi()),
24+
message,
25+
"try",
26+
"flatten()".to_string(),
27+
Applicability::MachineApplicable,
28+
);
29+
};
30+
31+
if_chain! {
32+
if let hir::ExprKind::Closure(_, _, body_id, _, _) = arg_node;
33+
let body = cx.tcx.hir().body(*body_id);
34+
35+
if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind;
36+
if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = body.value.kind;
37+
38+
if path.segments.len() == 1;
39+
if path.segments[0].ident.name == binding_ident.name;
40+
41+
then {
42+
apply_lint("called `filter_map(|x| x)` on an `Iterator`");
43+
}
44+
}
45+
46+
if_chain! {
47+
if let hir::ExprKind::Path(ref qpath) = arg_node;
48+
49+
if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY);
50+
51+
then {
52+
apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`");
53+
}
54+
}
55+
}
56+
}

clippy_lints/src/methods/mod.rs

+29-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod bind_instead_of_map;
2+
mod filter_map_identity;
23
mod inefficient_to_string;
34
mod inspect_for_each;
45
mod manual_saturating_arithmetic;
@@ -1466,6 +1467,29 @@ declare_clippy_lint! {
14661467
"using `.inspect().for_each()`, which can be replaced with `.for_each()`"
14671468
}
14681469

1470+
declare_clippy_lint! {
1471+
/// **What it does:** Checks for usage of `filter_map(|x| x)`.
1472+
///
1473+
/// **Why is this bad?** Readability, this can be written more concisely by using `flatten`.
1474+
///
1475+
/// **Known problems:** None.
1476+
///
1477+
/// **Example:**
1478+
///
1479+
/// ```rust
1480+
/// # let iter = vec![Some(1)].into_iter();
1481+
/// iter.filter_map(|x| x);
1482+
/// ```
1483+
/// Use instead:
1484+
/// ```rust
1485+
/// # let iter = vec![Some(1)].into_iter();
1486+
/// iter.flatten();
1487+
/// ```
1488+
pub FILTER_MAP_IDENTITY,
1489+
complexity,
1490+
"call to `filter_map` where `flatten` is sufficient"
1491+
}
1492+
14691493
pub struct Methods {
14701494
msrv: Option<RustcVersion>,
14711495
}
@@ -1503,6 +1527,7 @@ impl_lint_pass!(Methods => [
15031527
FILTER_NEXT,
15041528
SKIP_WHILE_NEXT,
15051529
FILTER_MAP,
1530+
FILTER_MAP_IDENTITY,
15061531
MANUAL_FILTER_MAP,
15071532
MANUAL_FIND_MAP,
15081533
FILTER_MAP_NEXT,
@@ -1596,7 +1621,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
15961621
["as_ref"] => lint_asref(cx, expr, "as_ref", arg_lists[0]),
15971622
["as_mut"] => lint_asref(cx, expr, "as_mut", arg_lists[0]),
15981623
["fold", ..] => lint_unnecessary_fold(cx, expr, arg_lists[0], method_spans[0]),
1599-
["filter_map", ..] => unnecessary_filter_map::lint(cx, expr, arg_lists[0]),
1624+
["filter_map", ..] => {
1625+
unnecessary_filter_map::lint(cx, expr, arg_lists[0]);
1626+
filter_map_identity::check(cx, expr, arg_lists[0], method_spans[0]);
1627+
},
16001628
["count", "map"] => lint_suspicious_map(cx, expr),
16011629
["assume_init"] => lint_maybe_uninit(cx, &arg_lists[0][0], expr),
16021630
["unwrap_or", arith @ ("checked_add" | "checked_sub" | "checked_mul")] => {

tests/ui/filter_map_identity.fixed

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// run-rustfix
2+
3+
#![allow(unused_imports)]
4+
#![warn(clippy::filter_map_identity)]
5+
6+
fn main() {
7+
let iterator = vec![Some(1), None, Some(2)].into_iter();
8+
let _ = iterator.flatten();
9+
10+
let iterator = vec![Some(1), None, Some(2)].into_iter();
11+
let _ = iterator.flatten();
12+
13+
use std::convert::identity;
14+
let iterator = vec![Some(1), None, Some(2)].into_iter();
15+
let _ = iterator.flatten();
16+
}

tests/ui/filter_map_identity.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// run-rustfix
2+
3+
#![allow(unused_imports)]
4+
#![warn(clippy::filter_map_identity)]
5+
6+
fn main() {
7+
let iterator = vec![Some(1), None, Some(2)].into_iter();
8+
let _ = iterator.filter_map(|x| x);
9+
10+
let iterator = vec![Some(1), None, Some(2)].into_iter();
11+
let _ = iterator.filter_map(std::convert::identity);
12+
13+
use std::convert::identity;
14+
let iterator = vec![Some(1), None, Some(2)].into_iter();
15+
let _ = iterator.filter_map(identity);
16+
}

tests/ui/filter_map_identity.stderr

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error: called `filter_map(|x| x)` on an `Iterator`
2+
--> $DIR/filter_map_identity.rs:8:22
3+
|
4+
LL | let _ = iterator.filter_map(|x| x);
5+
| ^^^^^^^^^^^^^^^^^ help: try: `flatten()`
6+
|
7+
= note: `-D clippy::filter-map-identity` implied by `-D warnings`
8+
9+
error: called `filter_map(std::convert::identity)` on an `Iterator`
10+
--> $DIR/filter_map_identity.rs:11:22
11+
|
12+
LL | let _ = iterator.filter_map(std::convert::identity);
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
14+
15+
error: called `filter_map(std::convert::identity)` on an `Iterator`
16+
--> $DIR/filter_map_identity.rs:15:22
17+
|
18+
LL | let _ = iterator.filter_map(identity);
19+
| ^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
20+
21+
error: aborting due to 3 previous errors
22+

0 commit comments

Comments
 (0)