@@ -330,6 +330,24 @@ declare_clippy_lint! {
330330 "using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`"
331331}
332332
333+ declare_clippy_lint ! {
334+ /// **What it does:** Checks for usage of `_.map_or(None, Some)`.
335+ ///
336+ /// **Why is this bad?** Readability, this can be written more concisely as
337+ /// `_.ok()`.
338+ ///
339+ /// **Known problems:**
340+ ///
341+ /// **Example:**
342+ /// ```rust
343+ /// # let opt = Some(1);
344+ /// # let r = opt.map_or(None, Some);
345+ /// ```
346+ pub RESULT_MAP_OR_INTO_OPTION ,
347+ style,
348+ "using `Result.map_or(None, Some)`, which is more succinctly expressed as `ok()`"
349+ }
350+
333351declare_clippy_lint ! {
334352 /// **What it does:** Checks for usage of `_.and_then(|x| Some(y))`.
335353 ///
@@ -1248,6 +1266,7 @@ declare_lint_pass!(Methods => [
12481266 OPTION_MAP_UNWRAP_OR ,
12491267 OPTION_MAP_UNWRAP_OR_ELSE ,
12501268 RESULT_MAP_UNWRAP_OR_ELSE ,
1269+ RESULT_MAP_OR_INTO_OPTION ,
12511270 OPTION_MAP_OR_NONE ,
12521271 OPTION_AND_THEN_SOME ,
12531272 OR_FUN_CALL ,
@@ -2517,37 +2536,73 @@ fn lint_map_unwrap_or_else<'a, 'tcx>(
25172536 }
25182537}
25192538
2520- /// lint use of `_.map_or(None, _)` for `Option`s
2539+ /// lint use of `_.map_or(None, _)` for `Option`s and `Result`s
25212540fn lint_map_or_none < ' a , ' tcx > (
25222541 cx : & LateContext < ' a , ' tcx > ,
25232542 expr : & ' tcx hir:: Expr < ' _ > ,
25242543 map_or_args : & ' tcx [ hir:: Expr < ' _ > ] ,
25252544) {
2526- if match_type ( cx, cx. tables . expr_ty ( & map_or_args[ 0 ] ) , & paths:: OPTION ) {
2527- // check if the first non-self argument to map_or() is None
2528- let map_or_arg_is_none = if let hir:: ExprKind :: Path ( ref qpath) = map_or_args[ 1 ] . kind {
2529- match_qpath ( qpath, & paths:: OPTION_NONE )
2530- } else {
2531- false
2532- } ;
2545+ let is_option = match_type ( cx, cx. tables . expr_ty ( & map_or_args[ 0 ] ) , & paths:: OPTION ) ;
2546+ let is_result = match_type ( cx, cx. tables . expr_ty ( & map_or_args[ 0 ] ) , & paths:: RESULT ) ;
2547+
2548+ // There are two variants of this `map_or` lint:
2549+ // (1) using `map_or` as an adapter from `Result<T,E>` to `Option<T>`
2550+ // (2) using `map_or` as a combinator instead of `and_then`
2551+ //
2552+ // (For this lint) we don't care if any other type calls `map_or`
2553+ if !is_option && !is_result {
2554+ return ;
2555+ }
25332556
2534- if map_or_arg_is_none {
2535- // lint message
2536- let msg = "called `map_or(None, f)` on an `Option` value. This can be done more directly by calling \
2537- `and_then(f)` instead";
2538- let map_or_self_snippet = snippet ( cx, map_or_args[ 0 ] . span , ".." ) ;
2539- let map_or_func_snippet = snippet ( cx, map_or_args[ 2 ] . span , ".." ) ;
2540- let hint = format ! ( "{0}.and_then({1})" , map_or_self_snippet, map_or_func_snippet) ;
2541- span_lint_and_sugg (
2542- cx,
2543- OPTION_MAP_OR_NONE ,
2544- expr. span ,
2545- msg,
2546- "try using `and_then` instead" ,
2547- hint,
2548- Applicability :: MachineApplicable ,
2549- ) ;
2550- }
2557+ let default_arg_is_none = if let hir:: ExprKind :: Path ( ref qpath) = map_or_args[ 1 ] . kind {
2558+ match_qpath ( qpath, & paths:: OPTION_NONE )
2559+ } else {
2560+ false
2561+ } ;
2562+
2563+ // This is really only needed if `is_result` holds. Computing it here
2564+ // makes `mess`'s assignment a bit easier, so just compute it here.
2565+ let f_arg_is_some = if let hir:: ExprKind :: Path ( ref qpath) = map_or_args[ 2 ] . kind {
2566+ match_qpath ( qpath, & paths:: OPTION_SOME )
2567+ } else {
2568+ false
2569+ } ;
2570+
2571+ let mess = if is_option && default_arg_is_none {
2572+ let self_snippet = snippet ( cx, map_or_args[ 0 ] . span , ".." ) ;
2573+ let func_snippet = snippet ( cx, map_or_args[ 2 ] . span , ".." ) ;
2574+ let msg = "called `map_or(None, f)` on an `Option` value. This can be done more directly by calling \
2575+ `and_then(f)` instead";
2576+ Some ( (
2577+ OPTION_MAP_OR_NONE ,
2578+ msg,
2579+ "try using `and_then` instead" ,
2580+ format ! ( "{0}.and_then({1})" , self_snippet, func_snippet) ,
2581+ ) )
2582+ } else if is_result && f_arg_is_some {
2583+ let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \
2584+ `ok()` instead";
2585+ let self_snippet = snippet ( cx, map_or_args[ 0 ] . span , ".." ) ;
2586+ Some ( (
2587+ RESULT_MAP_OR_INTO_OPTION ,
2588+ msg,
2589+ "try using `ok` instead" ,
2590+ format ! ( "{0}.ok()" , self_snippet) ,
2591+ ) )
2592+ } else {
2593+ None
2594+ } ;
2595+
2596+ if let Some ( ( lint, msg, instead, hint) ) = mess {
2597+ span_lint_and_sugg (
2598+ cx,
2599+ lint,
2600+ expr. span ,
2601+ msg,
2602+ instead,
2603+ hint,
2604+ Applicability :: MachineApplicable ,
2605+ ) ;
25512606 }
25522607}
25532608
0 commit comments