@@ -288,6 +288,50 @@ declare_clippy_lint! {
288288 "using combinations of `filter`, `map`, `filter_map` and `flat_map` which can usually be written as a single method call"
289289}
290290
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+
291335declare_clippy_lint ! {
292336 /// **What it does:** Checks for an iterator search (such as `find()`,
293337 /// `position()`, or `rposition()`) followed by a call to `is_some()`.
@@ -798,6 +842,8 @@ declare_lint_pass!(Methods => [
798842 TEMPORARY_CSTRING_AS_PTR ,
799843 FILTER_NEXT ,
800844 FILTER_MAP ,
845+ FILTER_MAP_NEXT ,
846+ FIND_MAP ,
801847 MAP_FLATTEN ,
802848 ITER_NTH ,
803849 ITER_SKIP_NEXT ,
@@ -833,6 +879,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
833879 [ "next" , "filter" ] => lint_filter_next ( cx, expr, arg_lists[ 1 ] ) ,
834880 [ "map" , "filter" ] => lint_filter_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
835881 [ "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 ] ) ,
836884 [ "flat_map" , "filter" ] => lint_filter_flat_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
837885 [ "flat_map" , "filter_map" ] => lint_filter_map_flat_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
838886 [ "flatten" , "map" ] => lint_map_flatten ( cx, expr, arg_lists[ 1 ] ) ,
@@ -1911,6 +1959,42 @@ fn lint_filter_map<'a, 'tcx>(
19111959 }
19121960}
19131961
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+
19141998/// lint use of `filter().map()` for `Iterators`
19151999fn lint_filter_map_map < ' a , ' tcx > (
19162000 cx : & LateContext < ' a , ' tcx > ,
0 commit comments