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