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
This commit is contained in:
commit
35a7095d8c
@ -5689,6 +5689,7 @@ Released 2018-09-13
|
|||||||
[`manual_unwrap_or_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or_default
|
[`manual_unwrap_or_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or_default
|
||||||
[`manual_while_let_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_while_let_some
|
[`manual_while_let_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_while_let_some
|
||||||
[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
|
[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
|
||||||
|
[`map_all_any_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_all_any_identity
|
||||||
[`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
|
[`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
|
||||||
[`map_collect_result_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_collect_result_unit
|
[`map_collect_result_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_collect_result_unit
|
||||||
[`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry
|
[`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry
|
||||||
|
@ -268,8 +268,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx
|
|||||||
if !snippet
|
if !snippet
|
||||||
.split("->")
|
.split("->")
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.map(|s| snippet_eq_ty(s, cast_from) || s.split("where").any(|ty| snippet_eq_ty(ty, cast_from)))
|
.any(|s| snippet_eq_ty(s, cast_from) || s.split("where").any(|ty| snippet_eq_ty(ty, cast_from)))
|
||||||
.any(|a| a)
|
|
||||||
{
|
{
|
||||||
return ControlFlow::Break(());
|
return ControlFlow::Break(());
|
||||||
}
|
}
|
||||||
|
@ -416,6 +416,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||||||
crate::methods::MANUAL_SPLIT_ONCE_INFO,
|
crate::methods::MANUAL_SPLIT_ONCE_INFO,
|
||||||
crate::methods::MANUAL_STR_REPEAT_INFO,
|
crate::methods::MANUAL_STR_REPEAT_INFO,
|
||||||
crate::methods::MANUAL_TRY_FOLD_INFO,
|
crate::methods::MANUAL_TRY_FOLD_INFO,
|
||||||
|
crate::methods::MAP_ALL_ANY_IDENTITY_INFO,
|
||||||
crate::methods::MAP_CLONE_INFO,
|
crate::methods::MAP_CLONE_INFO,
|
||||||
crate::methods::MAP_COLLECT_RESULT_UNIT_INFO,
|
crate::methods::MAP_COLLECT_RESULT_UNIT_INFO,
|
||||||
crate::methods::MAP_ERR_IGNORE_INFO,
|
crate::methods::MAP_ERR_IGNORE_INFO,
|
||||||
|
43
clippy_lints/src/methods/map_all_any_identity.rs
Normal file
43
clippy_lints/src/methods/map_all_any_identity.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
|
use clippy_utils::source::SpanRangeExt;
|
||||||
|
use clippy_utils::{is_expr_identity_function, is_trait_method};
|
||||||
|
use rustc_errors::Applicability;
|
||||||
|
use rustc_hir::Expr;
|
||||||
|
use rustc_lint::LateContext;
|
||||||
|
use rustc_span::{Span, sym};
|
||||||
|
|
||||||
|
use super::MAP_ALL_ANY_IDENTITY;
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub(super) fn check(
|
||||||
|
cx: &LateContext<'_>,
|
||||||
|
expr: &Expr<'_>,
|
||||||
|
recv: &Expr<'_>,
|
||||||
|
map_call_span: Span,
|
||||||
|
map_arg: &Expr<'_>,
|
||||||
|
any_call_span: Span,
|
||||||
|
any_arg: &Expr<'_>,
|
||||||
|
method: &str,
|
||||||
|
) {
|
||||||
|
if is_trait_method(cx, expr, sym::Iterator)
|
||||||
|
&& is_trait_method(cx, recv, sym::Iterator)
|
||||||
|
&& is_expr_identity_function(cx, any_arg)
|
||||||
|
&& let map_any_call_span = map_call_span.with_hi(any_call_span.hi())
|
||||||
|
&& let Some(map_arg) = map_arg.span.get_source_text(cx)
|
||||||
|
{
|
||||||
|
span_lint_and_then(
|
||||||
|
cx,
|
||||||
|
MAP_ALL_ANY_IDENTITY,
|
||||||
|
map_any_call_span,
|
||||||
|
format!("usage of `.map(...).{method}(identity)`"),
|
||||||
|
|diag| {
|
||||||
|
diag.span_suggestion_verbose(
|
||||||
|
map_any_call_span,
|
||||||
|
format!("use `.{method}(...)` instead"),
|
||||||
|
format!("{method}({map_arg})"),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -60,6 +60,7 @@ mod manual_ok_or;
|
|||||||
mod manual_saturating_arithmetic;
|
mod manual_saturating_arithmetic;
|
||||||
mod manual_str_repeat;
|
mod manual_str_repeat;
|
||||||
mod manual_try_fold;
|
mod manual_try_fold;
|
||||||
|
mod map_all_any_identity;
|
||||||
mod map_clone;
|
mod map_clone;
|
||||||
mod map_collect_result_unit;
|
mod map_collect_result_unit;
|
||||||
mod map_err_ignore;
|
mod map_err_ignore;
|
||||||
@ -4167,29 +4168,54 @@ declare_clippy_lint! {
|
|||||||
"calling `.first().is_some()` or `.first().is_none()` instead of `.is_empty()`"
|
"calling `.first().is_some()` or `.first().is_none()` instead of `.is_empty()`"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// It detects useless calls to `str::as_bytes()` before calling `len()` or `is_empty()`.
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// The `len()` and `is_empty()` methods are also directly available on strings, and they
|
||||||
|
/// return identical results. In particular, `len()` on a string returns the number of
|
||||||
|
/// bytes.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```
|
||||||
|
/// let len = "some string".as_bytes().len();
|
||||||
|
/// let b = "some string".as_bytes().is_empty();
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```
|
||||||
|
/// let len = "some string".len();
|
||||||
|
/// let b = "some string".is_empty();
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.84.0"]
|
||||||
|
pub NEEDLESS_AS_BYTES,
|
||||||
|
complexity,
|
||||||
|
"detect useless calls to `as_bytes()`"
|
||||||
|
}
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
/// It detects useless calls to `str::as_bytes()` before calling `len()` or `is_empty()`.
|
/// Checks for usage of `.map(…)`, followed by `.all(identity)` or `.any(identity)`.
|
||||||
///
|
///
|
||||||
/// ### Why is this bad?
|
/// ### Why is this bad?
|
||||||
/// The `len()` and `is_empty()` methods are also directly available on strings, and they
|
/// The `.all(…)` or `.any(…)` methods can be called directly in place of `.map(…)`.
|
||||||
/// return identical results. In particular, `len()` on a string returns the number of
|
|
||||||
/// bytes.
|
|
||||||
///
|
///
|
||||||
/// ### Example
|
/// ### Example
|
||||||
/// ```
|
/// ```
|
||||||
/// let len = "some string".as_bytes().len();
|
/// # let mut v = [""];
|
||||||
/// let b = "some string".as_bytes().is_empty();
|
/// let e1 = v.iter().map(|s| s.is_empty()).all(|a| a);
|
||||||
|
/// let e2 = v.iter().map(|s| s.is_empty()).any(std::convert::identity);
|
||||||
/// ```
|
/// ```
|
||||||
/// Use instead:
|
/// Use instead:
|
||||||
/// ```
|
/// ```
|
||||||
/// let len = "some string".len();
|
/// # let mut v = [""];
|
||||||
/// let b = "some string".is_empty();
|
/// let e1 = v.iter().all(|s| s.is_empty());
|
||||||
|
/// let e2 = v.iter().any(|s| s.is_empty());
|
||||||
/// ```
|
/// ```
|
||||||
#[clippy::version = "1.84.0"]
|
#[clippy::version = "1.84.0"]
|
||||||
pub NEEDLESS_AS_BYTES,
|
pub MAP_ALL_ANY_IDENTITY,
|
||||||
complexity,
|
complexity,
|
||||||
"detect useless calls to `as_bytes()`"
|
"combine `.map(_)` followed by `.all(identity)`/`.any(identity)` into a single call"
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Methods {
|
pub struct Methods {
|
||||||
@ -4354,6 +4380,7 @@ impl_lint_pass!(Methods => [
|
|||||||
MANUAL_INSPECT,
|
MANUAL_INSPECT,
|
||||||
UNNECESSARY_MIN_OR_MAX,
|
UNNECESSARY_MIN_OR_MAX,
|
||||||
NEEDLESS_AS_BYTES,
|
NEEDLESS_AS_BYTES,
|
||||||
|
MAP_ALL_ANY_IDENTITY,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/// Extracts a method call name, args, and `Span` of the method name.
|
/// Extracts a method call name, args, and `Span` of the method name.
|
||||||
@ -4561,15 +4588,21 @@ impl Methods {
|
|||||||
("all", [arg]) => {
|
("all", [arg]) => {
|
||||||
unused_enumerate_index::check(cx, expr, recv, arg);
|
unused_enumerate_index::check(cx, expr, recv, arg);
|
||||||
needless_character_iteration::check(cx, expr, recv, arg, true);
|
needless_character_iteration::check(cx, expr, recv, arg, true);
|
||||||
if let Some(("cloned", recv2, [], _, _)) = method_call(recv) {
|
match method_call(recv) {
|
||||||
iter_overeager_cloned::check(
|
Some(("cloned", recv2, [], _, _)) => {
|
||||||
cx,
|
iter_overeager_cloned::check(
|
||||||
expr,
|
cx,
|
||||||
recv,
|
expr,
|
||||||
recv2,
|
recv,
|
||||||
iter_overeager_cloned::Op::NeedlessMove(arg),
|
recv2,
|
||||||
false,
|
iter_overeager_cloned::Op::NeedlessMove(arg),
|
||||||
);
|
false,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
Some(("map", _, [map_arg], _, map_call_span)) => {
|
||||||
|
map_all_any_identity::check(cx, expr, recv, map_call_span, map_arg, call_span, arg, "all");
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
("and_then", [arg]) => {
|
("and_then", [arg]) => {
|
||||||
@ -4598,6 +4631,9 @@ impl Methods {
|
|||||||
{
|
{
|
||||||
string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv);
|
string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv);
|
||||||
},
|
},
|
||||||
|
Some(("map", _, [map_arg], _, map_call_span)) => {
|
||||||
|
map_all_any_identity::check(cx, expr, recv, map_call_span, map_arg, call_span, arg, "any");
|
||||||
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
21
tests/ui/map_all_any_identity.fixed
Normal file
21
tests/ui/map_all_any_identity.fixed
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#![warn(clippy::map_all_any_identity)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _ = ["foo"].into_iter().any(|s| s == "foo");
|
||||||
|
//~^ map_all_any_identity
|
||||||
|
let _ = ["foo"].into_iter().all(|s| s == "foo");
|
||||||
|
//~^ map_all_any_identity
|
||||||
|
|
||||||
|
//
|
||||||
|
// Do not lint
|
||||||
|
//
|
||||||
|
// Not identity
|
||||||
|
let _ = ["foo"].into_iter().map(|s| s.len()).any(|n| n > 0);
|
||||||
|
// Macro
|
||||||
|
macro_rules! map {
|
||||||
|
($x:expr) => {
|
||||||
|
$x.into_iter().map(|s| s == "foo")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
map!(["foo"]).any(|a| a);
|
||||||
|
}
|
21
tests/ui/map_all_any_identity.rs
Normal file
21
tests/ui/map_all_any_identity.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#![warn(clippy::map_all_any_identity)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _ = ["foo"].into_iter().map(|s| s == "foo").any(|a| a);
|
||||||
|
//~^ map_all_any_identity
|
||||||
|
let _ = ["foo"].into_iter().map(|s| s == "foo").all(std::convert::identity);
|
||||||
|
//~^ map_all_any_identity
|
||||||
|
|
||||||
|
//
|
||||||
|
// Do not lint
|
||||||
|
//
|
||||||
|
// Not identity
|
||||||
|
let _ = ["foo"].into_iter().map(|s| s.len()).any(|n| n > 0);
|
||||||
|
// Macro
|
||||||
|
macro_rules! map {
|
||||||
|
($x:expr) => {
|
||||||
|
$x.into_iter().map(|s| s == "foo")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
map!(["foo"]).any(|a| a);
|
||||||
|
}
|
26
tests/ui/map_all_any_identity.stderr
Normal file
26
tests/ui/map_all_any_identity.stderr
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
error: usage of `.map(...).any(identity)`
|
||||||
|
--> tests/ui/map_all_any_identity.rs:4:33
|
||||||
|
|
|
||||||
|
LL | let _ = ["foo"].into_iter().map(|s| s == "foo").any(|a| a);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `-D clippy::map-all-any-identity` implied by `-D warnings`
|
||||||
|
= help: to override `-D warnings` add `#[allow(clippy::map_all_any_identity)]`
|
||||||
|
help: use `.any(...)` instead
|
||||||
|
|
|
||||||
|
LL | let _ = ["foo"].into_iter().any(|s| s == "foo");
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
error: usage of `.map(...).all(identity)`
|
||||||
|
--> tests/ui/map_all_any_identity.rs:6:33
|
||||||
|
|
|
||||||
|
LL | let _ = ["foo"].into_iter().map(|s| s == "foo").all(std::convert::identity);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: use `.all(...)` instead
|
||||||
|
|
|
||||||
|
LL | let _ = ["foo"].into_iter().all(|s| s == "foo");
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user