2021-03-25 13:29:11 -05:00
|
|
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
|
|
|
use clippy_utils::source::snippet;
|
|
|
|
use clippy_utils::ty::is_type_diagnostic_item;
|
2022-02-10 11:40:06 -06:00
|
|
|
use clippy_utils::{is_lang_ctor, path_def_id};
|
2021-03-12 08:30:50 -06:00
|
|
|
use rustc_errors::Applicability;
|
|
|
|
use rustc_hir as hir;
|
2021-04-22 04:31:13 -05:00
|
|
|
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
2021-03-12 08:30:50 -06:00
|
|
|
use rustc_lint::LateContext;
|
2022-02-10 11:40:06 -06:00
|
|
|
use rustc_middle::ty::DefIdTree;
|
2021-03-12 08:30:50 -06:00
|
|
|
use rustc_span::symbol::sym;
|
|
|
|
|
|
|
|
use super::OPTION_MAP_OR_NONE;
|
|
|
|
use super::RESULT_MAP_OR_INTO_OPTION;
|
|
|
|
|
2021-12-06 05:33:31 -06:00
|
|
|
// The expression inside a closure may or may not have surrounding braces
|
|
|
|
// which causes problems when generating a suggestion.
|
2022-03-14 06:02:53 -05:00
|
|
|
fn reduce_unit_expression<'a>(expr: &'a hir::Expr<'_>) -> Option<(&'a hir::Expr<'a>, &'a [hir::Expr<'a>])> {
|
2021-12-06 05:33:31 -06:00
|
|
|
match expr.kind {
|
|
|
|
hir::ExprKind::Call(func, arg_char) => Some((func, arg_char)),
|
|
|
|
hir::ExprKind::Block(block, _) => {
|
|
|
|
match (block.stmts, block.expr) {
|
|
|
|
(&[], Some(inner_expr)) => {
|
|
|
|
// If block only contains an expression,
|
|
|
|
// reduce `|x| { x + 1 }` to `|x| x + 1`
|
2022-03-14 06:02:53 -05:00
|
|
|
reduce_unit_expression(inner_expr)
|
2021-12-06 05:33:31 -06:00
|
|
|
},
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-12 08:30:50 -06:00
|
|
|
/// lint use of `_.map_or(None, _)` for `Option`s and `Result`s
|
2021-04-08 10:50:13 -05:00
|
|
|
pub(super) fn check<'tcx>(
|
|
|
|
cx: &LateContext<'tcx>,
|
|
|
|
expr: &'tcx hir::Expr<'_>,
|
|
|
|
recv: &'tcx hir::Expr<'_>,
|
|
|
|
def_arg: &'tcx hir::Expr<'_>,
|
|
|
|
map_arg: &'tcx hir::Expr<'_>,
|
|
|
|
) {
|
2021-10-02 18:51:01 -05:00
|
|
|
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option);
|
|
|
|
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
|
2021-03-12 08:30:50 -06:00
|
|
|
|
|
|
|
// There are two variants of this `map_or` lint:
|
|
|
|
// (1) using `map_or` as an adapter from `Result<T,E>` to `Option<T>`
|
|
|
|
// (2) using `map_or` as a combinator instead of `and_then`
|
|
|
|
//
|
|
|
|
// (For this lint) we don't care if any other type calls `map_or`
|
|
|
|
if !is_option && !is_result {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-12-06 05:33:31 -06:00
|
|
|
let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind {
|
|
|
|
is_lang_ctor(cx, qpath, OptionNone)
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
};
|
2021-03-12 08:30:50 -06:00
|
|
|
|
2021-12-06 05:33:31 -06:00
|
|
|
if !default_arg_is_none {
|
|
|
|
// nothing to lint!
|
|
|
|
return;
|
|
|
|
}
|
2021-03-12 08:30:50 -06:00
|
|
|
|
2021-12-06 05:33:31 -06:00
|
|
|
let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind {
|
|
|
|
is_lang_ctor(cx, qpath, OptionSome)
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
};
|
|
|
|
|
|
|
|
if is_option {
|
|
|
|
let self_snippet = snippet(cx, recv.span, "..");
|
|
|
|
if_chain! {
|
2022-06-30 05:18:51 -05:00
|
|
|
if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) = map_arg.kind;
|
2022-06-11 14:25:25 -05:00
|
|
|
let arg_snippet = snippet(cx, fn_decl_span, "..");
|
|
|
|
let body = cx.tcx.hir().body(body);
|
2022-09-08 14:27:09 -05:00
|
|
|
if let Some((func, [arg_char])) = reduce_unit_expression(body.value);
|
2022-06-11 14:25:25 -05:00
|
|
|
if let Some(id) = path_def_id(cx, func).map(|ctor_id| cx.tcx.parent(ctor_id));
|
|
|
|
if Some(id) == cx.tcx.lang_items().option_some_variant();
|
|
|
|
then {
|
|
|
|
let func_snippet = snippet(cx, arg_char.span, "..");
|
|
|
|
let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
|
|
|
|
`map(..)` instead";
|
|
|
|
return span_lint_and_sugg(
|
|
|
|
cx,
|
|
|
|
OPTION_MAP_OR_NONE,
|
|
|
|
expr.span,
|
|
|
|
msg,
|
|
|
|
"try using `map` instead",
|
|
|
|
format!("{0}.map({1} {2})", self_snippet, arg_snippet,func_snippet),
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
);
|
|
|
|
}
|
2021-03-12 08:30:50 -06:00
|
|
|
}
|
|
|
|
|
2021-12-06 05:33:31 -06:00
|
|
|
let func_snippet = snippet(cx, map_arg.span, "..");
|
|
|
|
let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
|
|
|
|
`and_then(..)` instead";
|
2022-06-04 06:34:07 -05:00
|
|
|
span_lint_and_sugg(
|
2021-12-06 05:33:31 -06:00
|
|
|
cx,
|
|
|
|
OPTION_MAP_OR_NONE,
|
|
|
|
expr.span,
|
|
|
|
msg,
|
|
|
|
"try using `and_then` instead",
|
|
|
|
format!("{0}.and_then({1})", self_snippet, func_snippet),
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
);
|
|
|
|
} else if f_arg_is_some {
|
|
|
|
let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \
|
|
|
|
`ok()` instead";
|
|
|
|
let self_snippet = snippet(cx, recv.span, "..");
|
2022-06-04 06:34:07 -05:00
|
|
|
span_lint_and_sugg(
|
2021-12-06 05:33:31 -06:00
|
|
|
cx,
|
|
|
|
RESULT_MAP_OR_INTO_OPTION,
|
|
|
|
expr.span,
|
|
|
|
msg,
|
|
|
|
"try using `ok` instead",
|
|
|
|
format!("{0}.ok()", self_snippet),
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
);
|
|
|
|
}
|
2021-03-12 08:30:50 -06:00
|
|
|
}
|