@@ -264,6 +264,32 @@ declare_clippy_lint! {
264
264
"using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`"
265
265
}
266
266
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
+
267
293
declare_clippy_lint ! {
268
294
/// **What it does:** Checks for usage of `_.filter(_).next()`.
269
295
///
@@ -900,6 +926,7 @@ declare_lint_pass!(Methods => [
900
926
OPTION_MAP_UNWRAP_OR_ELSE ,
901
927
RESULT_MAP_UNWRAP_OR_ELSE ,
902
928
OPTION_MAP_OR_NONE ,
929
+ OPTION_AND_THEN_SOME ,
903
930
OR_FUN_CALL ,
904
931
EXPECT_FUN_CALL ,
905
932
CHARS_NEXT_CMP ,
@@ -948,6 +975,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
948
975
[ "unwrap_or" , "map" ] => option_map_unwrap_or:: lint ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
949
976
[ "unwrap_or_else" , "map" ] => lint_map_unwrap_or_else ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
950
977
[ "map_or" , ..] => lint_map_or_none ( cx, expr, arg_lists[ 0 ] ) ,
978
+ [ "and_then" , ..] => lint_option_and_then_some ( cx, expr, arg_lists[ 0 ] ) ,
951
979
[ "next" , "filter" ] => lint_filter_next ( cx, expr, arg_lists[ 1 ] ) ,
952
980
[ "map" , "filter" ] => lint_filter_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
953
981
[ "map" , "filter_map" ] => lint_filter_map_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
@@ -2052,6 +2080,57 @@ fn lint_map_or_none<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr,
2052
2080
}
2053
2081
}
2054
2082
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
+
2092
+ match args[ 1 ] . node {
2093
+ hir:: ExprKind :: Closure ( _, _, body_id, closure_args_span, _) => {
2094
+ let closure_body = cx. tcx . hir ( ) . body ( body_id) ;
2095
+ let closure_expr = remove_blocks ( & closure_body. value ) ;
2096
+
2097
+ // let note = format!("{:?}", args[1]);
2098
+ // span_note_and_lint(cx, OPTION_AND_THEN_SOME, closure_args_span, "Outer debugging
2099
+ // information", closure_args_span, ¬e);
2100
+
2101
+ if let hir:: ExprKind :: Call ( ref some_expr, ref some_args) = closure_expr. node {
2102
+ if let hir:: ExprKind :: Path ( ref qpath) = some_expr. node {
2103
+ if match_qpath ( qpath, & paths:: OPTION_SOME ) {
2104
+ if some_args. len ( ) == 1 {
2105
+ let inner_expr = & some_args[ 0 ] ;
2106
+ let some_inner_snip = snippet ( cx, inner_expr. span , ".." ) ;
2107
+ let closure_args_snip = snippet ( cx, closure_args_span, ".." ) ;
2108
+ let option_snip = snippet ( cx, args[ 0 ] . span , ".." ) ;
2109
+ let note = format ! ( "{}.map({} {})" , option_snip, closure_args_snip, some_inner_snip) ;
2110
+ span_lint_and_sugg (
2111
+ cx,
2112
+ OPTION_AND_THEN_SOME ,
2113
+ expr. span ,
2114
+ LINT_MSG ,
2115
+ "try this" ,
2116
+ note,
2117
+ Applicability :: MachineApplicable ,
2118
+ ) ;
2119
+ }
2120
+ }
2121
+ }
2122
+ }
2123
+ } ,
2124
+ // hir::ExprKind::Path(ref qpath) => {
2125
+ // if match_qpath(qpath, &paths::OPTION_SOME) {
2126
+ // let note = format!("{:?}", args[1]);
2127
+ // span_note_and_lint(cx, OPTION_AND_THEN_SOME, args[1].span, "Some debugging information",
2128
+ // args[1].span, ¬e); }
2129
+ // },
2130
+ _ => { } ,
2131
+ }
2132
+ }
2133
+
2055
2134
/// lint use of `filter().next()` for `Iterators`
2056
2135
fn lint_filter_next < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , expr : & ' tcx hir:: Expr , filter_args : & ' tcx [ hir:: Expr ] ) {
2057
2136
// lint if caller of `.filter().next()` is an Iterator
0 commit comments