@@ -38,6 +38,7 @@ mod into_iter_on_ref;
38
38
mod is_digit_ascii_radix;
39
39
mod iter_cloned_collect;
40
40
mod iter_count;
41
+ mod iter_filter;
41
42
mod iter_kv_map;
42
43
mod iter_next_slice;
43
44
mod iter_nth;
@@ -1175,7 +1176,7 @@ declare_clippy_lint! {
1175
1176
1176
1177
declare_clippy_lint ! {
1177
1178
/// ### 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
1179
1180
/// be replaced with a `.flatten()` call.
1180
1181
///
1181
1182
/// ### Why is this bad?
@@ -3755,7 +3756,7 @@ declare_clippy_lint! {
3755
3756
3756
3757
declare_clippy_lint ! {
3757
3758
/// ### 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
3759
3760
/// be replaced with a `.flatten()` call.
3760
3761
///
3761
3762
/// ### Why is this bad?
@@ -3776,6 +3777,58 @@ declare_clippy_lint! {
3776
3777
"filtering `Result` for `Ok` then force-unwrapping, which can be one type-safe operation"
3777
3778
}
3778
3779
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
+
3779
3832
pub struct Methods {
3780
3833
avoid_breaking_exported_api : bool ,
3781
3834
msrv : Msrv ,
@@ -3928,6 +3981,8 @@ impl_lint_pass!(Methods => [
3928
3981
JOIN_ABSOLUTE_PATHS ,
3929
3982
OPTION_MAP_OR_ERR_OK ,
3930
3983
RESULT_FILTER_MAP ,
3984
+ ITER_FILTER_IS_SOME ,
3985
+ ITER_FILTER_IS_OK ,
3931
3986
] ) ;
3932
3987
3933
3988
/// Extracts a method call name, args, and `Span` of the method name.
@@ -4257,7 +4312,24 @@ impl Methods {
4257
4312
string_extend_chars:: check ( cx, expr, recv, arg) ;
4258
4313
extend_with_drain:: check ( cx, expr, recv, arg) ;
4259
4314
} ,
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] ) => {
4261
4333
if let Some ( ( "cloned" , recv2, [ ] , _span2, _) ) = method_call ( recv) {
4262
4334
// if `arg` has side-effect, the semantic will change
4263
4335
iter_overeager_cloned:: check (
0 commit comments