]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
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 | } |