@@ -288,6 +288,50 @@ declare_clippy_lint! {
288
288
"using combinations of `filter`, `map`, `filter_map` and `flat_map` which can usually be written as a single method call"
289
289
}
290
290
291
+ declare_clippy_lint ! {
292
+ /// **What it does:** Checks for usage of `_.filter_map(_).next()`.
293
+ ///
294
+ /// **Why is this bad?** Readability, this can be written more concisely as a
295
+ /// single method call.
296
+ ///
297
+ /// **Known problems:** None
298
+ ///
299
+ /// **Example:**
300
+ /// ```rust
301
+ /// (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next();
302
+ /// ```
303
+ /// Can be written as
304
+ ///
305
+ /// ```rust
306
+ /// (0..3).find_map(|x| if x == 2 { Some(x) } else { None });
307
+ /// ```
308
+ pub FILTER_MAP_NEXT ,
309
+ pedantic,
310
+ "using combination of `filter_map` and `next` which can usually be written as a single method call"
311
+ }
312
+
313
+ declare_clippy_lint ! {
314
+ /// **What it does:** Checks for usage of `_.find(_).map(_)`.
315
+ ///
316
+ /// **Why is this bad?** Readability, this can be written more concisely as a
317
+ /// single method call.
318
+ ///
319
+ /// **Known problems:** Often requires a condition + Option/Iterator creation
320
+ /// inside the closure.
321
+ ///
322
+ /// **Example:**
323
+ /// ```rust
324
+ /// (0..3).find(|x| x == 2).map(|x| x * 2);
325
+ /// ```
326
+ /// Can be written as
327
+ /// ```rust
328
+ /// (0..3).find_map(|x| if x == 2 { Some(x * 2) } else { None });
329
+ /// ```
330
+ pub FIND_MAP ,
331
+ pedantic,
332
+ "using a combination of `find` and `map` can usually be written as a single method call"
333
+ }
334
+
291
335
declare_clippy_lint ! {
292
336
/// **What it does:** Checks for an iterator search (such as `find()`,
293
337
/// `position()`, or `rposition()`) followed by a call to `is_some()`.
@@ -798,6 +842,8 @@ declare_lint_pass!(Methods => [
798
842
TEMPORARY_CSTRING_AS_PTR ,
799
843
FILTER_NEXT ,
800
844
FILTER_MAP ,
845
+ FILTER_MAP_NEXT ,
846
+ FIND_MAP ,
801
847
MAP_FLATTEN ,
802
848
ITER_NTH ,
803
849
ITER_SKIP_NEXT ,
@@ -833,6 +879,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
833
879
[ "next" , "filter" ] => lint_filter_next ( cx, expr, arg_lists[ 1 ] ) ,
834
880
[ "map" , "filter" ] => lint_filter_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
835
881
[ "map" , "filter_map" ] => lint_filter_map_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
882
+ [ "next" , "filter_map" ] => lint_filter_map_next ( cx, expr, arg_lists[ 1 ] ) ,
883
+ [ "map" , "find" ] => lint_find_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
836
884
[ "flat_map" , "filter" ] => lint_filter_flat_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
837
885
[ "flat_map" , "filter_map" ] => lint_filter_map_flat_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
838
886
[ "flatten" , "map" ] => lint_map_flatten ( cx, expr, arg_lists[ 1 ] ) ,
@@ -1911,6 +1959,42 @@ fn lint_filter_map<'a, 'tcx>(
1911
1959
}
1912
1960
}
1913
1961
1962
+ /// lint use of `filter_map().next()` for `Iterators`
1963
+ fn lint_filter_map_next < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , expr : & ' tcx hir:: Expr , filter_args : & ' tcx [ hir:: Expr ] ) {
1964
+ if match_trait_method ( cx, expr, & paths:: ITERATOR ) {
1965
+ let msg = "called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling \
1966
+ `.find_map(p)` instead.";
1967
+ let filter_snippet = snippet ( cx, filter_args[ 1 ] . span , ".." ) ;
1968
+ if filter_snippet. lines ( ) . count ( ) <= 1 {
1969
+ span_note_and_lint (
1970
+ cx,
1971
+ FILTER_MAP_NEXT ,
1972
+ expr. span ,
1973
+ msg,
1974
+ expr. span ,
1975
+ & format ! ( "replace `filter_map({0}).next()` with `find_map({0})`" , filter_snippet) ,
1976
+ ) ;
1977
+ } else {
1978
+ span_lint ( cx, FILTER_MAP_NEXT , expr. span , msg) ;
1979
+ }
1980
+ }
1981
+ }
1982
+
1983
+ /// lint use of `find().map()` for `Iterators`
1984
+ fn lint_find_map < ' a , ' tcx > (
1985
+ cx : & LateContext < ' a , ' tcx > ,
1986
+ expr : & ' tcx hir:: Expr ,
1987
+ _find_args : & ' tcx [ hir:: Expr ] ,
1988
+ map_args : & ' tcx [ hir:: Expr ] ,
1989
+ ) {
1990
+ // lint if caller of `.filter().map()` is an Iterator
1991
+ if match_trait_method ( cx, & map_args[ 0 ] , & paths:: ITERATOR ) {
1992
+ let msg = "called `find(p).map(q)` on an `Iterator`. \
1993
+ This is more succinctly expressed by calling `.find_map(..)` instead.";
1994
+ span_lint ( cx, FIND_MAP , expr. span , msg) ;
1995
+ }
1996
+ }
1997
+
1914
1998
/// lint use of `filter().map()` for `Iterators`
1915
1999
fn lint_filter_map_map < ' a , ' tcx > (
1916
2000
cx : & LateContext < ' a , ' tcx > ,
0 commit comments