Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New lint missing_iterator_fold #12211

Closed
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5360,6 +5360,7 @@ Released 2018-09-13
[`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc
[`missing_fields_in_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_fields_in_debug
[`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items
[`missing_iterator_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_iterator_fold
[`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc
[`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
[`missing_spin_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_spin_loop
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO,
crate::missing_fields_in_debug::MISSING_FIELDS_IN_DEBUG_INFO,
crate::missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS_INFO,
crate::missing_iterator_fold::MISSING_ITERATOR_FOLD_INFO,
crate::missing_trait_methods::MISSING_TRAIT_METHODS_INFO,
crate::mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION_INFO,
crate::mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION_INFO,
Expand Down
2 changes: 2 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ mod missing_doc;
mod missing_enforced_import_rename;
mod missing_fields_in_debug;
mod missing_inline;
mod missing_iterator_fold;
mod missing_trait_methods;
mod mixed_read_write_in_expression;
mod module_style;
Expand Down Expand Up @@ -1105,6 +1106,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
});
store.register_late_pass(move |_| Box::new(incompatible_msrv::IncompatibleMsrv::new(msrv())));
store.register_late_pass(|_| Box::new(to_string_trait_impl::ToStringTraitImpl));
store.register_late_pass(|_| Box::new(missing_iterator_fold::MissingIteratorFold));
// add lints here, do not remove this comment, it's used in `new_lint`
}

Expand Down
83 changes: 83 additions & 0 deletions clippy_lints/src/missing_iterator_fold.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use clippy_utils::diagnostics::span_lint;
use rustc_hir::{AssocItemKind, Impl, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::declare_lint_pass;
use rustc_span::sym;

declare_clippy_lint! {
/// ### What it does
/// Checks for `Iterator` implementations not specializing `fold`.
///
/// ### Why is this bad?
/// Specialize `fold` might provide more performant internal iteration.
/// Methods relying on `fold` would then be faster as well.
/// By default, methods that consume the entire iterator rely on it such as `for_each`, `count`...
///
/// ### Example
/// ```no_run
/// struct MyIter(u32);
///
/// impl Iterator for MyIter {
/// type Item = u32;
/// fn next(&mut self) -> Option<Self::Item> {
/// # todo!()
/// // ...
/// }
/// }
/// ```
/// Use instead:
/// ```no_run
/// struct MyIter(u32);
///
/// impl Iterator for MyIter {
/// type Item = u32;
/// fn next(&mut self) -> Option<Self::Item> {
/// # todo!()
/// // ...
/// }
/// fn fold<B, F>(self, init: B, f: F) -> B
/// where
/// F: FnMut(B, Self::Item) -> B,
/// {
/// todo!()
/// }
/// }
/// ```
#[clippy::version = "1.77.0"]
pub MISSING_ITERATOR_FOLD,
restriction,
"a missing `Iterator::fold` specialization"
}

declare_lint_pass!(MissingIteratorFold => [MISSING_ITERATOR_FOLD]);

impl LateLintPass<'_> for MissingIteratorFold {
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
if in_external_macro(cx.sess(), item.span) {
return;
}
if let ItemKind::Impl(Impl {
of_trait: Some(trait_ref),
..
}) = &item.kind
&& let Some(trait_id) = trait_ref.trait_def_id()
&& cx.tcx.is_diagnostic_item(sym::Iterator, trait_id)
{
let has_fold = item
.expect_impl()
.items
.iter()
.filter(|assoc| matches!(assoc.kind, AssocItemKind::Fn { .. }))
.any(|assoc| assoc.ident.name.as_str() == "fold");
if !has_fold {
span_lint(
cx,
MISSING_ITERATOR_FOLD,
item.span,
"you are implementing `Iterator` without specializing its `fold` method",
);
}
}
}
}
30 changes: 30 additions & 0 deletions tests/ui/missing_iterator_fold.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#![warn(clippy::missing_iterator_fold)]

struct Countdown(u8);

impl Iterator for Countdown {
type Item = u8;

fn next(&mut self) -> Option<u8> {
self.0 = self.0.checked_sub(1)?;
Some(self.0)
}
}

struct Countdown2(u8);

impl Iterator for Countdown2 {
type Item = u8;

fn next(&mut self) -> Option<u8> {
self.0 = self.0.checked_sub(1)?;
Some(self.0)
}

fn fold<B, F>(self, init: B, f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
(0..self.0).rfold(init, f)
}
}
17 changes: 17 additions & 0 deletions tests/ui/missing_iterator_fold.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error: you are implementing `Iterator` without specializing its `fold` method
--> $DIR/missing_iterator_fold.rs:5:1
|
LL | / impl Iterator for Countdown {
LL | | type Item = u8;
LL | |
LL | | fn next(&mut self) -> Option<u8> {
... |
LL | | }
LL | | }
| |_^
|
= note: `-D clippy::missing-iterator-fold` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::missing_iterator_fold)]`

error: aborting due to 1 previous error

Loading