@@ -38,6 +38,7 @@ mod into_iter_on_ref;
3838mod is_digit_ascii_radix;
3939mod iter_cloned_collect;
4040mod iter_count;
41+ mod iter_filter;
4142mod iter_kv_map;
4243mod iter_next_slice;
4344mod iter_nth;
@@ -1175,7 +1176,7 @@ declare_clippy_lint! {
11751176
11761177declare_clippy_lint ! {
11771178 /// ### What it does
1178- /// Checks for iterators of `Option`s using `` .filter(Option::is_some).map(Option::unwrap)` that may
1179+ /// Checks for iterators of `Option`s using `.filter(Option::is_some).map(Option::unwrap)` that may
11791180 /// be replaced with a `.flatten()` call.
11801181 ///
11811182 /// ### Why is this bad?
@@ -3755,7 +3756,7 @@ declare_clippy_lint! {
37553756
37563757declare_clippy_lint ! {
37573758 /// ### What it does
3758- /// Checks for iterators of `Result`s using `` .filter(Result::is_ok).map(Result::unwrap)` that may
3759+ /// Checks for iterators of `Result`s using `.filter(Result::is_ok).map(Result::unwrap)` that may
37593760 /// be replaced with a `.flatten()` call.
37603761 ///
37613762 /// ### Why is this bad?
@@ -3776,6 +3777,58 @@ declare_clippy_lint! {
37763777 "filtering `Result` for `Ok` then force-unwrapping, which can be one type-safe operation"
37773778}
37783779
3780+ declare_clippy_lint ! {
3781+ /// ### What it does
3782+ /// Checks for usage of `.filter(Option::is_some)` that may be replaced with a `.flatten()` call.
3783+ /// This lint will require additional changes to the follow-up calls as it appects the type.
3784+ ///
3785+ /// ### Why is this bad?
3786+ /// This pattern is often followed by manual unwrapping of the `Option`. The simplification
3787+ /// results in more readable and succint code without the need for manual unwrapping.
3788+ ///
3789+ /// ### Example
3790+ /// ```no_run
3791+ /// // example code where clippy issues a warning
3792+ /// vec![Some(1)].into_iter().filter(Option::is_some);
3793+ ///
3794+ /// ```
3795+ /// Use instead:
3796+ /// ```no_run
3797+ /// // example code which does not raise clippy warning
3798+ /// vec![Some(1)].into_iter().flatten();
3799+ /// ```
3800+ #[ clippy:: version = "1.76.0" ]
3801+ pub ITER_FILTER_IS_SOME ,
3802+ pedantic,
3803+ "filtering an iterator over `Option`s for `Some` can be achieved with `flatten`"
3804+ }
3805+
3806+ declare_clippy_lint ! {
3807+ /// ### What it does
3808+ /// Checks for usage of `.filter(Result::is_ok)` that may be replaced with a `.flatten()` call.
3809+ /// This lint will require additional changes to the follow-up calls as it appects the type.
3810+ ///
3811+ /// ### Why is this bad?
3812+ /// This pattern is often followed by manual unwrapping of `Result`. The simplification
3813+ /// results in more readable and succint code without the need for manual unwrapping.
3814+ ///
3815+ /// ### Example
3816+ /// ```no_run
3817+ /// // example code where clippy issues a warning
3818+ /// vec![Ok::<i32, String>(1)].into_iter().filter(Result::is_ok);
3819+ ///
3820+ /// ```
3821+ /// Use instead:
3822+ /// ```no_run
3823+ /// // example code which does not raise clippy warning
3824+ /// vec![Ok::<i32, String>(1)].into_iter().flatten();
3825+ /// ```
3826+ #[ clippy:: version = "1.76.0" ]
3827+ pub ITER_FILTER_IS_OK ,
3828+ pedantic,
3829+ "filtering an iterator over `Result`s for `Ok` can be achieved with `flatten`"
3830+ }
3831+
37793832pub struct Methods {
37803833 avoid_breaking_exported_api : bool ,
37813834 msrv : Msrv ,
@@ -3928,6 +3981,8 @@ impl_lint_pass!(Methods => [
39283981 JOIN_ABSOLUTE_PATHS ,
39293982 OPTION_MAP_OR_ERR_OK ,
39303983 RESULT_FILTER_MAP ,
3984+ ITER_FILTER_IS_SOME ,
3985+ ITER_FILTER_IS_OK ,
39313986] ) ;
39323987
39333988/// Extracts a method call name, args, and `Span` of the method name.
@@ -4257,7 +4312,24 @@ impl Methods {
42574312 string_extend_chars:: check ( cx, expr, recv, arg) ;
42584313 extend_with_drain:: check ( cx, expr, recv, arg) ;
42594314 } ,
4260- ( name @ ( "filter" | "find" ) , [ arg] ) => {
4315+ ( "filter" , [ arg] ) => {
4316+ if let Some ( ( "cloned" , recv2, [ ] , _span2, _) ) = method_call ( recv) {
4317+ // if `arg` has side-effect, the semantic will change
4318+ iter_overeager_cloned:: check (
4319+ cx,
4320+ expr,
4321+ recv,
4322+ recv2,
4323+ iter_overeager_cloned:: Op :: FixClosure ( name, arg) ,
4324+ false ,
4325+ ) ;
4326+ }
4327+ if self . msrv . meets ( msrvs:: ITER_FLATTEN ) {
4328+ // use the sourcemap to get the span of the closure
4329+ iter_filter:: check ( cx, expr, arg, span) ;
4330+ }
4331+ } ,
4332+ ( "find" , [ arg] ) => {
42614333 if let Some ( ( "cloned" , recv2, [ ] , _span2, _) ) = method_call ( recv) {
42624334 // if `arg` has side-effect, the semantic will change
42634335 iter_overeager_cloned:: check (
0 commit comments