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;
7 use rustc_hir::LangItem::{OptionNone, OptionSome};
8 use rustc_lint::LateContext;
9 use rustc_span::symbol::sym;
11 use super::OPTION_MAP_OR_NONE;
12 use super::RESULT_MAP_OR_INTO_OPTION;
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>(
18 expr: &'a hir::Expr<'_>,
19 ) -> Option<(&'a hir::Expr<'a>, &'a [hir::Expr<'a>])> {
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)
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<'_>,
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);
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`
51 // (For this lint) we don't care if any other type calls `map_or`
52 if !is_option && !is_result {
56 let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind {
57 is_lang_ctor(cx, qpath, OptionNone)
62 if !default_arg_is_none {
67 let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind {
68 is_lang_ctor(cx, qpath, OptionSome)
74 let self_snippet = snippet(cx, recv.span, "..");
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;
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 \
88 return span_lint_and_sugg(
93 "try using `map` instead",
94 format!("{0}.map({1} {2})", self_snippet, arg_snippet,func_snippet),
95 Applicability::MachineApplicable,
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(
109 "try using `and_then` instead",
110 format!("{0}.and_then({1})", self_snippet, func_snippet),
111 Applicability::MachineApplicable,
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 \
116 let self_snippet = snippet(cx, recv.span, "..");
117 return span_lint_and_sugg(
119 RESULT_MAP_OR_INTO_OPTION,
122 "try using `ok` instead",
123 format!("{0}.ok()", self_snippet),
124 Applicability::MachineApplicable,