]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/methods/option_map_or_none.rs
Destructure args in methods module
[rust.git] / clippy_lints / src / methods / option_map_or_none.rs
1 use clippy_utils::diagnostics::span_lint_and_sugg;
2 use clippy_utils::source::snippet;
3 use clippy_utils::ty::is_type_diagnostic_item;
4 use clippy_utils::{match_qpath, paths};
5 use rustc_errors::Applicability;
6 use rustc_hir as hir;
7 use rustc_lint::LateContext;
8 use rustc_span::symbol::sym;
9
10 use super::OPTION_MAP_OR_NONE;
11 use super::RESULT_MAP_OR_INTO_OPTION;
12
13 /// lint use of `_.map_or(None, _)` for `Option`s and `Result`s
14 pub(super) fn check<'tcx>(
15     cx: &LateContext<'tcx>,
16     expr: &'tcx hir::Expr<'_>,
17     recv: &'tcx hir::Expr<'_>,
18     def_arg: &'tcx hir::Expr<'_>,
19     map_arg: &'tcx hir::Expr<'_>,
20 ) {
21     let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::option_type);
22     let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::result_type);
23
24     // There are two variants of this `map_or` lint:
25     // (1) using `map_or` as an adapter from `Result<T,E>` to `Option<T>`
26     // (2) using `map_or` as a combinator instead of `and_then`
27     //
28     // (For this lint) we don't care if any other type calls `map_or`
29     if !is_option && !is_result {
30         return;
31     }
32
33     let (lint_name, msg, instead, hint) = {
34         let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind {
35             match_qpath(qpath, &paths::OPTION_NONE)
36         } else {
37             return;
38         };
39
40         if !default_arg_is_none {
41             // nothing to lint!
42             return;
43         }
44
45         let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind {
46             match_qpath(qpath, &paths::OPTION_SOME)
47         } else {
48             false
49         };
50
51         if is_option {
52             let self_snippet = snippet(cx, recv.span, "..");
53             let func_snippet = snippet(cx, map_arg.span, "..");
54             let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
55                        `and_then(..)` instead";
56             (
57                 OPTION_MAP_OR_NONE,
58                 msg,
59                 "try using `and_then` instead",
60                 format!("{0}.and_then({1})", self_snippet, func_snippet),
61             )
62         } else if f_arg_is_some {
63             let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \
64                        `ok()` instead";
65             let self_snippet = snippet(cx, recv.span, "..");
66             (
67                 RESULT_MAP_OR_INTO_OPTION,
68                 msg,
69                 "try using `ok` instead",
70                 format!("{0}.ok()", self_snippet),
71             )
72         } else {
73             // nothing to lint!
74             return;
75         }
76     };
77
78     span_lint_and_sugg(
79         cx,
80         lint_name,
81         expr.span,
82         msg,
83         instead,
84         hint,
85         Applicability::MachineApplicable,
86     );
87 }