diff --git a/CHANGELOG.md b/CHANGELOG.md index fc51694ff57f..60cb34259260 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5995,6 +5995,7 @@ Released 2018-09-13 [`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute [`unstable_as_mut_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_mut_slice [`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice +[`unused_any_all`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_any_all [`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async [`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect [`unused_enumerate_index`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_enumerate_index diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index e478ab330e8b..8131f1cde55d 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -738,6 +738,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::unnecessary_wraps::UNNECESSARY_WRAPS_INFO, crate::unnested_or_patterns::UNNESTED_OR_PATTERNS_INFO, crate::unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME_INFO, + crate::unused_any_all::UNUSED_ANY_ALL_INFO, crate::unused_async::UNUSED_ASYNC_INFO, crate::unused_io_amount::UNUSED_IO_AMOUNT_INFO, crate::unused_peekable::UNUSED_PEEKABLE_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 58ad9f645d6a..0470177ebcc7 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -367,6 +367,7 @@ mod unnecessary_struct_initialization; mod unnecessary_wraps; mod unnested_or_patterns; mod unsafe_removed_from_name; +mod unused_any_all; mod unused_async; mod unused_io_amount; mod unused_peekable; @@ -799,6 +800,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(conf))); store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison)); store.register_early_pass(move || Box::new(module_style::ModStyle)); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |tcx| Box::new(disallowed_types::DisallowedTypes::new(tcx, conf))); store.register_late_pass(move |tcx| Box::new(missing_enforced_import_rename::ImportRename::new(tcx, conf))); diff --git a/clippy_lints/src/unused_any_all.rs b/clippy_lints/src/unused_any_all.rs new file mode 100644 index 000000000000..634d5a500f76 --- /dev/null +++ b/clippy_lints/src/unused_any_all.rs @@ -0,0 +1,95 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::macros::HirNode; +use rustc_hir::{Expr, ExprKind, QPath, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::impl_lint_pass; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `any` or `all` where the return values are ignored. + /// + /// Ignoring the return value of `any` or `all` is suspicious and may indicate a mistake. + /// The returned value is usually intended to be used as a condition. + /// If `any` or `all` is called solely for their side effects on the iterator or their `FnMut` argument, + /// it's generally better to consume the iterator using a plain `for` loop. + /// + /// ### Example + /// ```no_run + /// let mut days_without_accident = 0; + /// (0..).all(|day| { + /// if day % 5 == 0 { + /// return false + /// } + /// + /// days_without_accident += 1; + /// true + /// }); + /// ``` + + #[clippy::version = "1.82.0"] + pub UNUSED_ANY_ALL, + suspicious, + "unused result of `Iterator::any` or `Iterator::all`" +} + +#[derive(Default)] +pub struct UnusedAnyAll; + +impl_lint_pass!(UnusedAnyAll => [UNUSED_ANY_ALL]); + +impl<'tcx> LateLintPass<'tcx> for UnusedAnyAll { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + let sym_any = sym::any; + let sym_all = sym::all; + let sym_iter = sym::Iterator; + + let ExprKind::Block(block, _label) = &expr.kind else { + return; + }; + + for statement in block.stmts { + let StmtKind::Semi(semi) = statement.kind else { continue }; + + let method_name = match &semi.kind { + ExprKind::MethodCall(path, expr, _args, _span) + if path.ident.name == sym_any + || path.ident.name == sym_all && clippy_utils::is_trait_method(cx, expr, sym::Iterator) => + { + path.ident.name + }, + ExprKind::Call(path, _args) => match path.kind { + ExprKind::Path(QPath::Resolved(_, path)) + if path + .segments + .first() + .map(|s| s.ident.name) + .zip(path.segments.last().map(|s| s.ident.name)) + == Some((sym_iter, sym_all)) => + { + sym_all + }, + ExprKind::Path(QPath::Resolved(_, path)) + if path + .segments + .first() + .map(|s| s.ident.name) + .zip(path.segments.last().map(|s| s.ident.name)) + == Some((sym_iter, sym_any)) => + { + sym_any + }, + _ => continue, + }, + _ => continue, + }; + + span_lint( + cx, + UNUSED_ANY_ALL, + semi.span(), + format!("unused result of `Iterator::{method_name}`"), + ); + } + } +} diff --git a/tests/ui/infinite_iter.rs b/tests/ui/infinite_iter.rs index da95ba04b821..992c23d15d17 100644 --- a/tests/ui/infinite_iter.rs +++ b/tests/ui/infinite_iter.rs @@ -1,4 +1,4 @@ -#![allow(clippy::uninlined_format_args)] +#![allow(clippy::uninlined_format_args, clippy::unused_any_all)] use std::iter::repeat; fn square_is_lower_64(x: &u32) -> bool { diff --git a/tests/ui/needless_character_iteration.fixed b/tests/ui/needless_character_iteration.fixed index f0bf84a41d7e..5d39ca1b955c 100644 --- a/tests/ui/needless_character_iteration.fixed +++ b/tests/ui/needless_character_iteration.fixed @@ -1,5 +1,5 @@ #![warn(clippy::needless_character_iteration)] -#![allow(clippy::map_identity, clippy::unnecessary_operation)] +#![allow(clippy::map_identity, clippy::unnecessary_operation, clippy::unused_any_all)] #[derive(Default)] struct S { diff --git a/tests/ui/needless_character_iteration.rs b/tests/ui/needless_character_iteration.rs index 2805d2438b4a..3be2017ea6ac 100644 --- a/tests/ui/needless_character_iteration.rs +++ b/tests/ui/needless_character_iteration.rs @@ -1,5 +1,5 @@ #![warn(clippy::needless_character_iteration)] -#![allow(clippy::map_identity, clippy::unnecessary_operation)] +#![allow(clippy::map_identity, clippy::unnecessary_operation, clippy::unused_any_all)] #[derive(Default)] struct S { diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index bd83581bdd97..86aaeada1831 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -1,4 +1,10 @@ -#![allow(unused, clippy::needless_if, clippy::suspicious_map, clippy::iter_count)] +#![allow( + unused, + clippy::needless_if, + clippy::suspicious_map, + clippy::iter_count, + clippy::unused_any_all +)] use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList}; diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 6a81a767bbb6..337ed6dd2e4a 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -1,4 +1,10 @@ -#![allow(unused, clippy::needless_if, clippy::suspicious_map, clippy::iter_count)] +#![allow( + unused, + clippy::needless_if, + clippy::suspicious_map, + clippy::iter_count, + clippy::unused_any_all +)] use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList}; diff --git a/tests/ui/needless_collect.stderr b/tests/ui/needless_collect.stderr index ea317896d368..c22ff1a13d3d 100644 --- a/tests/ui/needless_collect.stderr +++ b/tests/ui/needless_collect.stderr @@ -1,5 +1,5 @@ error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:9:29 + --> tests/ui/needless_collect.rs:15:29 | LL | let len = sample.iter().collect::>().len(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` @@ -8,109 +8,109 @@ LL | let len = sample.iter().collect::>().len(); = help: to override `-D warnings` add `#[allow(clippy::needless_collect)]` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:10:22 + --> tests/ui/needless_collect.rs:16:22 | LL | if sample.iter().collect::>().is_empty() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:13:28 + --> tests/ui/needless_collect.rs:19:28 | LL | sample.iter().cloned().collect::>().contains(&1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == 1)` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:18:35 + --> tests/ui/needless_collect.rs:24:35 | LL | sample.iter().map(|x| (x, x)).collect::>().is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:19:35 + --> tests/ui/needless_collect.rs:25:35 | LL | sample.iter().map(|x| (x, x)).collect::>().is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:26:19 + --> tests/ui/needless_collect.rs:32:19 | LL | sample.iter().collect::>().len(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:27:19 + --> tests/ui/needless_collect.rs:33:19 | LL | sample.iter().collect::>().is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:28:28 + --> tests/ui/needless_collect.rs:34:28 | LL | sample.iter().cloned().collect::>().contains(&1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == 1)` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:29:19 + --> tests/ui/needless_collect.rs:35:19 | LL | sample.iter().collect::>().contains(&&1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &1)` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:32:19 + --> tests/ui/needless_collect.rs:38:19 | LL | sample.iter().collect::>().len(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:33:19 + --> tests/ui/needless_collect.rs:39:19 | LL | sample.iter().collect::>().is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:38:27 + --> tests/ui/needless_collect.rs:44:27 | LL | let _ = sample.iter().collect::>().is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:39:27 + --> tests/ui/needless_collect.rs:45:27 | LL | let _ = sample.iter().collect::>().contains(&&0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &0)` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:61:27 + --> tests/ui/needless_collect.rs:67:27 | LL | let _ = sample.iter().collect::>().is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:62:27 + --> tests/ui/needless_collect.rs:68:27 | LL | let _ = sample.iter().collect::>().contains(&&0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &0)` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:66:40 + --> tests/ui/needless_collect.rs:72:40 | LL | Vec::::new().extend((0..10).collect::>()); | ^^^^^^^^^^^^^^^^^^^^ help: remove this call error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:67:20 + --> tests/ui/needless_collect.rs:73:20 | LL | foo((0..10).collect::>()); | ^^^^^^^^^^^^^^^^^^^^ help: remove this call error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:68:49 + --> tests/ui/needless_collect.rs:74:49 | LL | bar((0..10).collect::>(), (0..10).collect::>()); | ^^^^^^^^^^^^^^^^^^^^ help: remove this call error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:69:37 + --> tests/ui/needless_collect.rs:75:37 | LL | baz((0..10), (), ('a'..='z').collect::>()) | ^^^^^^^^^^^^^^^^^^^^ help: remove this call diff --git a/tests/ui/string_lit_chars_any.fixed b/tests/ui/string_lit_chars_any.fixed index 03e20c16ee63..4f840524ce30 100644 --- a/tests/ui/string_lit_chars_any.fixed +++ b/tests/ui/string_lit_chars_any.fixed @@ -1,5 +1,11 @@ //@aux-build:proc_macros.rs -#![allow(clippy::eq_op, clippy::needless_raw_string_hashes, clippy::no_effect, unused)] +#![allow( + clippy::eq_op, + clippy::needless_raw_string_hashes, + clippy::no_effect, + clippy::unused_any_all, + unused +)] #![warn(clippy::string_lit_chars_any)] #[macro_use] diff --git a/tests/ui/string_lit_chars_any.rs b/tests/ui/string_lit_chars_any.rs index 12e6ffb6a9c4..8f94d0803b87 100644 --- a/tests/ui/string_lit_chars_any.rs +++ b/tests/ui/string_lit_chars_any.rs @@ -1,5 +1,11 @@ //@aux-build:proc_macros.rs -#![allow(clippy::eq_op, clippy::needless_raw_string_hashes, clippy::no_effect, unused)] +#![allow( + clippy::eq_op, + clippy::needless_raw_string_hashes, + clippy::no_effect, + clippy::unused_any_all, + unused +)] #![warn(clippy::string_lit_chars_any)] #[macro_use] diff --git a/tests/ui/string_lit_chars_any.stderr b/tests/ui/string_lit_chars_any.stderr index 4d3ca98e6237..593df9fa579b 100644 --- a/tests/ui/string_lit_chars_any.stderr +++ b/tests/ui/string_lit_chars_any.stderr @@ -1,5 +1,5 @@ error: usage of `.chars().any(...)` to check if a char matches any from a string literal - --> tests/ui/string_lit_chars_any.rs:18:5 + --> tests/ui/string_lit_chars_any.rs:24:5 | LL | "\\.+*?()|[]{}^$#&-~".chars().any(|x| x == c); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -12,7 +12,7 @@ LL | matches!(c, '\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: usage of `.chars().any(...)` to check if a char matches any from a string literal - --> tests/ui/string_lit_chars_any.rs:19:5 + --> tests/ui/string_lit_chars_any.rs:25:5 | LL | r#"\.+*?()|[]{}^$#&-~"#.chars().any(|x| x == c); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | matches!(c, '\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: usage of `.chars().any(...)` to check if a char matches any from a string literal - --> tests/ui/string_lit_chars_any.rs:20:5 + --> tests/ui/string_lit_chars_any.rs:26:5 | LL | "\\.+*?()|[]{}^$#&-~".chars().any(|x| c == x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -34,7 +34,7 @@ LL | matches!(c, '\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: usage of `.chars().any(...)` to check if a char matches any from a string literal - --> tests/ui/string_lit_chars_any.rs:21:5 + --> tests/ui/string_lit_chars_any.rs:27:5 | LL | r#"\.+*?()|[]{}^$#&-~"#.chars().any(|x| c == x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | matches!(c, '\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: usage of `.chars().any(...)` to check if a char matches any from a string literal - --> tests/ui/string_lit_chars_any.rs:23:5 + --> tests/ui/string_lit_chars_any.rs:29:5 | LL | "\\.+*?()|[]{}^$#&-~".chars().any(|x| { x == c }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unused_any_all.rs b/tests/ui/unused_any_all.rs new file mode 100644 index 000000000000..7912fe2c2293 --- /dev/null +++ b/tests/ui/unused_any_all.rs @@ -0,0 +1,8 @@ +#![warn(clippy::unused_any_all)] + +fn main() { + (0..1).any(|_| false); + Iterator::any(&mut (0..1), |_| false); + let _ = (0..1).any(|_| false); + let _ = Iterator::any(&mut (0..1), |_| false); +} diff --git a/tests/ui/unused_any_all.stderr b/tests/ui/unused_any_all.stderr new file mode 100644 index 000000000000..426b05fc3f79 --- /dev/null +++ b/tests/ui/unused_any_all.stderr @@ -0,0 +1,17 @@ +error: unused result of `Iterator::any` + --> tests/ui/unused_any_all.rs:4:5 + | +LL | (0..1).any(|_| false); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unused-any-all` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unused_any_all)]` + +error: unused result of `Iterator::any` + --> tests/ui/unused_any_all.rs:5:5 + | +LL | Iterator::any(&mut (0..1), |_| false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors +