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