@@ -264,6 +264,32 @@ declare_clippy_lint! {
264264 "using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`"
265265}
266266
267+ declare_clippy_lint ! {
268+ /// **What it does:** Checks for usage of `_.and_then(|x| Some(y))`.
269+ ///
270+ /// **Why is this bad?** Readability, this can be written more concisely as
271+ /// `_.map(|x| y)`.
272+ ///
273+ /// **Known problems:** None
274+ ///
275+ /// **Example:**
276+ ///
277+ /// ```rust
278+ /// let x = Some("foo");
279+ /// let _ = x.and_then(|s| Some(s.len()));
280+ /// ```
281+ ///
282+ /// The correct use would be:
283+ ///
284+ /// ```rust
285+ /// let x = Some("foo");
286+ /// let _ = x.map(|s| s.len());
287+ /// ```
288+ pub OPTION_AND_THEN_SOME ,
289+ complexity,
290+ "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`"
291+ }
292+
267293declare_clippy_lint ! {
268294 /// **What it does:** Checks for usage of `_.filter(_).next()`.
269295 ///
@@ -900,6 +926,7 @@ declare_lint_pass!(Methods => [
900926 OPTION_MAP_UNWRAP_OR_ELSE ,
901927 RESULT_MAP_UNWRAP_OR_ELSE ,
902928 OPTION_MAP_OR_NONE ,
929+ OPTION_AND_THEN_SOME ,
903930 OR_FUN_CALL ,
904931 EXPECT_FUN_CALL ,
905932 CHARS_NEXT_CMP ,
@@ -948,6 +975,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
948975 [ "unwrap_or" , "map" ] => option_map_unwrap_or:: lint ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
949976 [ "unwrap_or_else" , "map" ] => lint_map_unwrap_or_else ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
950977 [ "map_or" , ..] => lint_map_or_none ( cx, expr, arg_lists[ 0 ] ) ,
978+ [ "and_then" , ..] => lint_option_and_then_some ( cx, expr, arg_lists[ 0 ] ) ,
951979 [ "next" , "filter" ] => lint_filter_next ( cx, expr, arg_lists[ 1 ] ) ,
952980 [ "map" , "filter" ] => lint_filter_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
953981 [ "map" , "filter_map" ] => lint_filter_map_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
@@ -2052,6 +2080,68 @@ fn lint_map_or_none<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr,
20522080 }
20532081}
20542082
2083+ /// Lint use of `_.and_then(|x| Some(y))` for `Option`s
2084+ fn lint_option_and_then_some ( cx : & LateContext < ' _ , ' _ > , expr : & hir:: Expr , args : & [ hir:: Expr ] ) {
2085+ let ty = cx. tables . expr_ty ( & args[ 0 ] ) ;
2086+ if !match_type ( cx, ty, & paths:: OPTION ) {
2087+ return ;
2088+ }
2089+
2090+ const LINT_MSG : & str = "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`" ;
2091+ const NO_OP_MSG : & str = "using `Option.and_then(Some)`, which is no-op" ;
2092+
2093+ match args[ 1 ] . node {
2094+ hir:: ExprKind :: Closure ( _, _, body_id, closure_args_span, _) => {
2095+ let closure_body = cx. tcx . hir ( ) . body ( body_id) ;
2096+ let closure_expr = remove_blocks ( & closure_body. value ) ;
2097+
2098+ // let note = format!("{:?}", args[1]);
2099+ // span_note_and_lint(cx, OPTION_AND_THEN_SOME, closure_args_span, "Outer debugging
2100+ // information", closure_args_span, ¬e);
2101+
2102+ if let hir:: ExprKind :: Call ( ref some_expr, ref some_args) = closure_expr. node {
2103+ if let hir:: ExprKind :: Path ( ref qpath) = some_expr. node {
2104+ if match_qpath ( qpath, & paths:: OPTION_SOME ) {
2105+ if some_args. len ( ) == 1 {
2106+ let inner_expr = & some_args[ 0 ] ;
2107+ let some_inner_snip = snippet ( cx, inner_expr. span , ".." ) ;
2108+ let closure_args_snip = snippet ( cx, closure_args_span, ".." ) ;
2109+ let option_snip = snippet ( cx, args[ 0 ] . span , ".." ) ;
2110+ let note = format ! ( "{}.map({} {})" , option_snip, closure_args_snip, some_inner_snip) ;
2111+ span_lint_and_sugg (
2112+ cx,
2113+ OPTION_AND_THEN_SOME ,
2114+ expr. span ,
2115+ LINT_MSG ,
2116+ "try this" ,
2117+ note,
2118+ Applicability :: MachineApplicable ,
2119+ ) ;
2120+ }
2121+ }
2122+ }
2123+ }
2124+ } ,
2125+ // `_.and_then(Some)` case, which is no-op.
2126+ hir:: ExprKind :: Path ( ref qpath) => {
2127+ if match_qpath ( qpath, & paths:: OPTION_SOME ) {
2128+ let option_snip = snippet ( cx, args[ 0 ] . span , ".." ) ;
2129+ let note = format ! ( "{}" , option_snip) ;
2130+ span_lint_and_sugg (
2131+ cx,
2132+ OPTION_AND_THEN_SOME ,
2133+ expr. span ,
2134+ NO_OP_MSG ,
2135+ "use the expression directly" ,
2136+ note,
2137+ Applicability :: MachineApplicable ,
2138+ ) ;
2139+ }
2140+ } ,
2141+ _ => { } ,
2142+ }
2143+ }
2144+
20552145/// lint use of `filter().next()` for `Iterators`
20562146fn lint_filter_next < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , expr : & ' tcx hir:: Expr , filter_args : & ' tcx [ hir:: Expr ] ) {
20572147 // lint if caller of `.filter().next()` is an Iterator
0 commit comments