]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs
Move `{core,std}::stream::Stream` to `{core,std}::async_iter::AsyncIterator`.
[rust.git] / src / tools / clippy / 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::{is_lang_ctor, single_segment_path};
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 // The expression inside a closure may or may not have surrounding braces
15 // which causes problems when generating a suggestion.
16 fn reduce_unit_expression<'a>(
17     cx: &LateContext<'_>,
18     expr: &'a hir::Expr<'_>,
19 ) -> Option<(&'a hir::Expr<'a>, &'a [hir::Expr<'a>])> {
20     match expr.kind {
21         hir::ExprKind::Call(func, arg_char) => Some((func, arg_char)),
22         hir::ExprKind::Block(block, _) => {
23             match (block.stmts, block.expr) {
24                 (&[], Some(inner_expr)) => {
25                     // If block only contains an expression,
26                     // reduce `|x| { x + 1 }` to `|x| x + 1`
27                     reduce_unit_expression(cx, inner_expr)
28                 },
29                 _ => None,
30             }
31         },
32         _ => None,
33     }
34 }
35
36 /// lint use of `_.map_or(None, _)` for `Option`s and `Result`s
37 pub(super) fn check<'tcx>(
38     cx: &LateContext<'tcx>,
39     expr: &'tcx hir::Expr<'_>,
40     recv: &'tcx hir::Expr<'_>,
41     def_arg: &'tcx hir::Expr<'_>,
42     map_arg: &'tcx hir::Expr<'_>,
43 ) {
44     let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option);
45     let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
46
47     // There are two variants of this `map_or` lint:
48     // (1) using `map_or` as an adapter from `Result<T,E>` to `Option<T>`
49     // (2) using `map_or` as a combinator instead of `and_then`
50     //
51     // (For this lint) we don't care if any other type calls `map_or`
52     if !is_option && !is_result {
53         return;
54     }
55
56     let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind {
57         is_lang_ctor(cx, qpath, OptionNone)
58     } else {
59         return;
60     };
61
62     if !default_arg_is_none {
63         // nothing to lint!
64         return;
65     }
66
67     let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind {
68         is_lang_ctor(cx, qpath, OptionSome)
69     } else {
70         false
71     };
72
73     if is_option {
74         let self_snippet = snippet(cx, recv.span, "..");
75         if_chain! {
76         if let hir::ExprKind::Closure(_, _, id, span, _) = map_arg.kind;
77             let arg_snippet = snippet(cx, span, "..");
78             let body = cx.tcx.hir().body(id);
79                 if let Some((func, arg_char)) = reduce_unit_expression(cx, &body.value);
80                 if arg_char.len() == 1;
81                 if let hir::ExprKind::Path(ref qpath) = func.kind;
82                 if let Some(segment) = single_segment_path(qpath);
83                 if segment.ident.name == sym::Some;
84                 then {
85                     let func_snippet = snippet(cx, arg_char[0].span, "..");
86                     let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
87                        `map(..)` instead";
88                     return span_lint_and_sugg(
89                         cx,
90                         OPTION_MAP_OR_NONE,
91                         expr.span,
92                         msg,
93                         "try using `map` instead",
94                         format!("{0}.map({1} {2})", self_snippet, arg_snippet,func_snippet),
95                         Applicability::MachineApplicable,
96                     );
97                 }
98
99         }
100
101         let func_snippet = snippet(cx, map_arg.span, "..");
102         let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
103                        `and_then(..)` instead";
104         return span_lint_and_sugg(
105             cx,
106             OPTION_MAP_OR_NONE,
107             expr.span,
108             msg,
109             "try using `and_then` instead",
110             format!("{0}.and_then({1})", self_snippet, func_snippet),
111             Applicability::MachineApplicable,
112         );
113     } else if f_arg_is_some {
114         let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \
115                        `ok()` instead";
116         let self_snippet = snippet(cx, recv.span, "..");
117         return span_lint_and_sugg(
118             cx,
119             RESULT_MAP_OR_INTO_OPTION,
120             expr.span,
121             msg,
122             "try using `ok` instead",
123             format!("{0}.ok()", self_snippet),
124             Applicability::MachineApplicable,
125         );
126     }
127 }