]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs
New upstream version 1.65.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / methods / option_map_or_none.rs
CommitLineData
cdc7bbd5 1use clippy_utils::diagnostics::span_lint_and_sugg;
cdc7bbd5
XL
2use clippy_utils::source::snippet;
3use clippy_utils::ty::is_type_diagnostic_item;
5099ac24 4use clippy_utils::{is_lang_ctor, path_def_id};
f20569fa
XL
5use rustc_errors::Applicability;
6use rustc_hir as hir;
cdc7bbd5 7use rustc_hir::LangItem::{OptionNone, OptionSome};
f20569fa 8use rustc_lint::LateContext;
5099ac24 9use rustc_middle::ty::DefIdTree;
f20569fa
XL
10use rustc_span::symbol::sym;
11
12use super::OPTION_MAP_OR_NONE;
13use super::RESULT_MAP_OR_INTO_OPTION;
14
a2a8927a
XL
15// The expression inside a closure may or may not have surrounding braces
16// which causes problems when generating a suggestion.
5e7ed085 17fn reduce_unit_expression<'a>(expr: &'a hir::Expr<'_>) -> Option<(&'a hir::Expr<'a>, &'a [hir::Expr<'a>])> {
a2a8927a
XL
18 match expr.kind {
19 hir::ExprKind::Call(func, arg_char) => Some((func, arg_char)),
20 hir::ExprKind::Block(block, _) => {
21 match (block.stmts, block.expr) {
22 (&[], Some(inner_expr)) => {
23 // If block only contains an expression,
24 // reduce `|x| { x + 1 }` to `|x| x + 1`
5e7ed085 25 reduce_unit_expression(inner_expr)
a2a8927a
XL
26 },
27 _ => None,
28 }
29 },
30 _ => None,
31 }
32}
33
f20569fa 34/// lint use of `_.map_or(None, _)` for `Option`s and `Result`s
cdc7bbd5
XL
35pub(super) fn check<'tcx>(
36 cx: &LateContext<'tcx>,
37 expr: &'tcx hir::Expr<'_>,
38 recv: &'tcx hir::Expr<'_>,
39 def_arg: &'tcx hir::Expr<'_>,
40 map_arg: &'tcx hir::Expr<'_>,
41) {
c295e0f8
XL
42 let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option);
43 let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
f20569fa
XL
44
45 // There are two variants of this `map_or` lint:
46 // (1) using `map_or` as an adapter from `Result<T,E>` to `Option<T>`
47 // (2) using `map_or` as a combinator instead of `and_then`
48 //
49 // (For this lint) we don't care if any other type calls `map_or`
50 if !is_option && !is_result {
51 return;
52 }
53
a2a8927a
XL
54 let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind {
55 is_lang_ctor(cx, qpath, OptionNone)
56 } else {
57 return;
58 };
f20569fa 59
a2a8927a
XL
60 if !default_arg_is_none {
61 // nothing to lint!
62 return;
63 }
f20569fa 64
a2a8927a
XL
65 let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind {
66 is_lang_ctor(cx, qpath, OptionSome)
67 } else {
68 false
69 };
70
71 if is_option {
72 let self_snippet = snippet(cx, recv.span, "..");
73 if_chain! {
064997fb 74 if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) = map_arg.kind;
923072b8
FG
75 let arg_snippet = snippet(cx, fn_decl_span, "..");
76 let body = cx.tcx.hir().body(body);
f2b60f7d 77 if let Some((func, [arg_char])) = reduce_unit_expression(body.value);
923072b8
FG
78 if let Some(id) = path_def_id(cx, func).map(|ctor_id| cx.tcx.parent(ctor_id));
79 if Some(id) == cx.tcx.lang_items().option_some_variant();
80 then {
81 let func_snippet = snippet(cx, arg_char.span, "..");
82 let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
83 `map(..)` instead";
84 return span_lint_and_sugg(
85 cx,
86 OPTION_MAP_OR_NONE,
87 expr.span,
88 msg,
89 "try using `map` instead",
90 format!("{0}.map({1} {2})", self_snippet, arg_snippet,func_snippet),
91 Applicability::MachineApplicable,
92 );
93 }
f20569fa 94 }
f20569fa 95
a2a8927a
XL
96 let func_snippet = snippet(cx, map_arg.span, "..");
97 let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
98 `and_then(..)` instead";
923072b8 99 span_lint_and_sugg(
a2a8927a
XL
100 cx,
101 OPTION_MAP_OR_NONE,
102 expr.span,
103 msg,
104 "try using `and_then` instead",
105 format!("{0}.and_then({1})", self_snippet, func_snippet),
106 Applicability::MachineApplicable,
107 );
108 } else if f_arg_is_some {
109 let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \
110 `ok()` instead";
111 let self_snippet = snippet(cx, recv.span, "..");
923072b8 112 span_lint_and_sugg(
a2a8927a
XL
113 cx,
114 RESULT_MAP_OR_INTO_OPTION,
115 expr.span,
116 msg,
117 "try using `ok` instead",
118 format!("{0}.ok()", self_snippet),
119 Applicability::MachineApplicable,
120 );
121 }
f20569fa 122}