Skip to content

Commit 35a7095

Browse files
committed
Auto merge of #13499 - samueltardieu:map-all-any-identity, r=xFrednet
New lint `map_all_any_identity` This lint has been inspired by code encountered in Clippy itself (see the first commit). changelog: [`map_all_any_identity`]: new lint
2 parents 625d391 + 91a1d16 commit 35a7095

8 files changed

+169
-21
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5689,6 +5689,7 @@ Released 2018-09-13
56895689
[`manual_unwrap_or_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or_default
56905690
[`manual_while_let_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_while_let_some
56915691
[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
5692+
[`map_all_any_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_all_any_identity
56925693
[`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
56935694
[`map_collect_result_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_collect_result_unit
56945695
[`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry

clippy_lints/src/casts/unnecessary_cast.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx
268268
if !snippet
269269
.split("->")
270270
.skip(1)
271-
.map(|s| snippet_eq_ty(s, cast_from) || s.split("where").any(|ty| snippet_eq_ty(ty, cast_from)))
272-
.any(|a| a)
271+
.any(|s| snippet_eq_ty(s, cast_from) || s.split("where").any(|ty| snippet_eq_ty(ty, cast_from)))
273272
{
274273
return ControlFlow::Break(());
275274
}

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
416416
crate::methods::MANUAL_SPLIT_ONCE_INFO,
417417
crate::methods::MANUAL_STR_REPEAT_INFO,
418418
crate::methods::MANUAL_TRY_FOLD_INFO,
419+
crate::methods::MAP_ALL_ANY_IDENTITY_INFO,
419420
crate::methods::MAP_CLONE_INFO,
420421
crate::methods::MAP_COLLECT_RESULT_UNIT_INFO,
421422
crate::methods::MAP_ERR_IGNORE_INFO,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use clippy_utils::diagnostics::span_lint_and_then;
2+
use clippy_utils::source::SpanRangeExt;
3+
use clippy_utils::{is_expr_identity_function, is_trait_method};
4+
use rustc_errors::Applicability;
5+
use rustc_hir::Expr;
6+
use rustc_lint::LateContext;
7+
use rustc_span::{Span, sym};
8+
9+
use super::MAP_ALL_ANY_IDENTITY;
10+
11+
#[allow(clippy::too_many_arguments)]
12+
pub(super) fn check(
13+
cx: &LateContext<'_>,
14+
expr: &Expr<'_>,
15+
recv: &Expr<'_>,
16+
map_call_span: Span,
17+
map_arg: &Expr<'_>,
18+
any_call_span: Span,
19+
any_arg: &Expr<'_>,
20+
method: &str,
21+
) {
22+
if is_trait_method(cx, expr, sym::Iterator)
23+
&& is_trait_method(cx, recv, sym::Iterator)
24+
&& is_expr_identity_function(cx, any_arg)
25+
&& let map_any_call_span = map_call_span.with_hi(any_call_span.hi())
26+
&& let Some(map_arg) = map_arg.span.get_source_text(cx)
27+
{
28+
span_lint_and_then(
29+
cx,
30+
MAP_ALL_ANY_IDENTITY,
31+
map_any_call_span,
32+
format!("usage of `.map(...).{method}(identity)`"),
33+
|diag| {
34+
diag.span_suggestion_verbose(
35+
map_any_call_span,
36+
format!("use `.{method}(...)` instead"),
37+
format!("{method}({map_arg})"),
38+
Applicability::MachineApplicable,
39+
);
40+
},
41+
);
42+
}
43+
}

clippy_lints/src/methods/mod.rs

+55-19
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ mod manual_ok_or;
6060
mod manual_saturating_arithmetic;
6161
mod manual_str_repeat;
6262
mod manual_try_fold;
63+
mod map_all_any_identity;
6364
mod map_clone;
6465
mod map_collect_result_unit;
6566
mod map_err_ignore;
@@ -4167,29 +4168,54 @@ declare_clippy_lint! {
41674168
"calling `.first().is_some()` or `.first().is_none()` instead of `.is_empty()`"
41684169
}
41694170

4171+
declare_clippy_lint! {
4172+
/// ### What it does
4173+
/// It detects useless calls to `str::as_bytes()` before calling `len()` or `is_empty()`.
4174+
///
4175+
/// ### Why is this bad?
4176+
/// The `len()` and `is_empty()` methods are also directly available on strings, and they
4177+
/// return identical results. In particular, `len()` on a string returns the number of
4178+
/// bytes.
4179+
///
4180+
/// ### Example
4181+
/// ```
4182+
/// let len = "some string".as_bytes().len();
4183+
/// let b = "some string".as_bytes().is_empty();
4184+
/// ```
4185+
/// Use instead:
4186+
/// ```
4187+
/// let len = "some string".len();
4188+
/// let b = "some string".is_empty();
4189+
/// ```
4190+
#[clippy::version = "1.84.0"]
4191+
pub NEEDLESS_AS_BYTES,
4192+
complexity,
4193+
"detect useless calls to `as_bytes()`"
4194+
}
4195+
41704196
declare_clippy_lint! {
41714197
/// ### What it does
4172-
/// It detects useless calls to `str::as_bytes()` before calling `len()` or `is_empty()`.
4198+
/// Checks for usage of `.map(…)`, followed by `.all(identity)` or `.any(identity)`.
41734199
///
41744200
/// ### Why is this bad?
4175-
/// The `len()` and `is_empty()` methods are also directly available on strings, and they
4176-
/// return identical results. In particular, `len()` on a string returns the number of
4177-
/// bytes.
4201+
/// The `.all(…)` or `.any(…)` methods can be called directly in place of `.map(…)`.
41784202
///
41794203
/// ### Example
41804204
/// ```
4181-
/// let len = "some string".as_bytes().len();
4182-
/// let b = "some string".as_bytes().is_empty();
4205+
/// # let mut v = [""];
4206+
/// let e1 = v.iter().map(|s| s.is_empty()).all(|a| a);
4207+
/// let e2 = v.iter().map(|s| s.is_empty()).any(std::convert::identity);
41834208
/// ```
41844209
/// Use instead:
41854210
/// ```
4186-
/// let len = "some string".len();
4187-
/// let b = "some string".is_empty();
4211+
/// # let mut v = [""];
4212+
/// let e1 = v.iter().all(|s| s.is_empty());
4213+
/// let e2 = v.iter().any(|s| s.is_empty());
41884214
/// ```
41894215
#[clippy::version = "1.84.0"]
4190-
pub NEEDLESS_AS_BYTES,
4216+
pub MAP_ALL_ANY_IDENTITY,
41914217
complexity,
4192-
"detect useless calls to `as_bytes()`"
4218+
"combine `.map(_)` followed by `.all(identity)`/`.any(identity)` into a single call"
41934219
}
41944220

41954221
pub struct Methods {
@@ -4354,6 +4380,7 @@ impl_lint_pass!(Methods => [
43544380
MANUAL_INSPECT,
43554381
UNNECESSARY_MIN_OR_MAX,
43564382
NEEDLESS_AS_BYTES,
4383+
MAP_ALL_ANY_IDENTITY,
43574384
]);
43584385

43594386
/// Extracts a method call name, args, and `Span` of the method name.
@@ -4561,15 +4588,21 @@ impl Methods {
45614588
("all", [arg]) => {
45624589
unused_enumerate_index::check(cx, expr, recv, arg);
45634590
needless_character_iteration::check(cx, expr, recv, arg, true);
4564-
if let Some(("cloned", recv2, [], _, _)) = method_call(recv) {
4565-
iter_overeager_cloned::check(
4566-
cx,
4567-
expr,
4568-
recv,
4569-
recv2,
4570-
iter_overeager_cloned::Op::NeedlessMove(arg),
4571-
false,
4572-
);
4591+
match method_call(recv) {
4592+
Some(("cloned", recv2, [], _, _)) => {
4593+
iter_overeager_cloned::check(
4594+
cx,
4595+
expr,
4596+
recv,
4597+
recv2,
4598+
iter_overeager_cloned::Op::NeedlessMove(arg),
4599+
false,
4600+
);
4601+
},
4602+
Some(("map", _, [map_arg], _, map_call_span)) => {
4603+
map_all_any_identity::check(cx, expr, recv, map_call_span, map_arg, call_span, arg, "all");
4604+
},
4605+
_ => {},
45734606
}
45744607
},
45754608
("and_then", [arg]) => {
@@ -4598,6 +4631,9 @@ impl Methods {
45984631
{
45994632
string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv);
46004633
},
4634+
Some(("map", _, [map_arg], _, map_call_span)) => {
4635+
map_all_any_identity::check(cx, expr, recv, map_call_span, map_arg, call_span, arg, "any");
4636+
},
46014637
_ => {},
46024638
}
46034639
},

tests/ui/map_all_any_identity.fixed

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#![warn(clippy::map_all_any_identity)]
2+
3+
fn main() {
4+
let _ = ["foo"].into_iter().any(|s| s == "foo");
5+
//~^ map_all_any_identity
6+
let _ = ["foo"].into_iter().all(|s| s == "foo");
7+
//~^ map_all_any_identity
8+
9+
//
10+
// Do not lint
11+
//
12+
// Not identity
13+
let _ = ["foo"].into_iter().map(|s| s.len()).any(|n| n > 0);
14+
// Macro
15+
macro_rules! map {
16+
($x:expr) => {
17+
$x.into_iter().map(|s| s == "foo")
18+
};
19+
}
20+
map!(["foo"]).any(|a| a);
21+
}

tests/ui/map_all_any_identity.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#![warn(clippy::map_all_any_identity)]
2+
3+
fn main() {
4+
let _ = ["foo"].into_iter().map(|s| s == "foo").any(|a| a);
5+
//~^ map_all_any_identity
6+
let _ = ["foo"].into_iter().map(|s| s == "foo").all(std::convert::identity);
7+
//~^ map_all_any_identity
8+
9+
//
10+
// Do not lint
11+
//
12+
// Not identity
13+
let _ = ["foo"].into_iter().map(|s| s.len()).any(|n| n > 0);
14+
// Macro
15+
macro_rules! map {
16+
($x:expr) => {
17+
$x.into_iter().map(|s| s == "foo")
18+
};
19+
}
20+
map!(["foo"]).any(|a| a);
21+
}

tests/ui/map_all_any_identity.stderr

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error: usage of `.map(...).any(identity)`
2+
--> tests/ui/map_all_any_identity.rs:4:33
3+
|
4+
LL | let _ = ["foo"].into_iter().map(|s| s == "foo").any(|a| a);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::map-all-any-identity` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::map_all_any_identity)]`
9+
help: use `.any(...)` instead
10+
|
11+
LL | let _ = ["foo"].into_iter().any(|s| s == "foo");
12+
| ~~~~~~~~~~~~~~~~~~~
13+
14+
error: usage of `.map(...).all(identity)`
15+
--> tests/ui/map_all_any_identity.rs:6:33
16+
|
17+
LL | let _ = ["foo"].into_iter().map(|s| s == "foo").all(std::convert::identity);
18+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
19+
|
20+
help: use `.all(...)` instead
21+
|
22+
LL | let _ = ["foo"].into_iter().all(|s| s == "foo");
23+
| ~~~~~~~~~~~~~~~~~~~
24+
25+
error: aborting due to 2 previous errors
26+

0 commit comments

Comments
 (0)