Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit ea73317

Browse files
Create new lint option_map_or_err_ok
1 parent 96eab06 commit ea73317

File tree

3 files changed

+70
-0
lines changed

3 files changed

+70
-0
lines changed

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
405405
crate::methods::OK_EXPECT_INFO,
406406
crate::methods::OPTION_AS_REF_DEREF_INFO,
407407
crate::methods::OPTION_FILTER_MAP_INFO,
408+
crate::methods::OPTION_MAP_OR_ERR_OK_INFO,
408409
crate::methods::OPTION_MAP_OR_NONE_INFO,
409410
crate::methods::OR_FUN_CALL_INFO,
410411
crate::methods::OR_THEN_UNWRAP_INFO,

clippy_lints/src/methods/mod.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ mod obfuscated_if_else;
7070
mod ok_expect;
7171
mod open_options;
7272
mod option_as_ref_deref;
73+
mod option_map_or_err_ok;
7374
mod option_map_or_none;
7475
mod option_map_unwrap_or;
7576
mod or_fun_call;
@@ -3726,6 +3727,31 @@ declare_clippy_lint! {
37263727
"calls to `Path::join` which will overwrite the original path"
37273728
}
37283729

3730+
declare_clippy_lint! {
3731+
/// ### What it does
3732+
/// Checks for usage of `_.map_or(Err(_), Ok)`.
3733+
///
3734+
/// ### Why is this bad?
3735+
/// Readability, this can be written more concisely as
3736+
/// `_.ok_or(_)`.
3737+
///
3738+
/// ### Example
3739+
/// ```no_run
3740+
/// # let opt = Some(1);
3741+
/// opt.map_or(Err("error"), Ok);
3742+
/// ```
3743+
///
3744+
/// Use instead:
3745+
/// ```no_run
3746+
/// # let opt = Some(1);
3747+
/// opt.ok_or("error");
3748+
/// ```
3749+
#[clippy::version = "1.76.0"]
3750+
pub OPTION_MAP_OR_ERR_OK,
3751+
style,
3752+
"using `Option.map_or(Err(_), Ok)`, which is more succinctly expressed as `Option.ok_or(_)`"
3753+
}
3754+
37293755
pub struct Methods {
37303756
avoid_breaking_exported_api: bool,
37313757
msrv: Msrv,
@@ -3876,6 +3902,7 @@ impl_lint_pass!(Methods => [
38763902
WAKER_CLONE_WAKE,
38773903
UNNECESSARY_FALLIBLE_CONVERSIONS,
38783904
JOIN_ABSOLUTE_PATHS,
3905+
OPTION_MAP_OR_ERR_OK,
38793906
]);
38803907

38813908
/// Extracts a method call name, args, and `Span` of the method name.
@@ -4335,6 +4362,7 @@ impl Methods {
43354362
("map_or", [def, map]) => {
43364363
option_map_or_none::check(cx, expr, recv, def, map);
43374364
manual_ok_or::check(cx, expr, recv, def, map);
4365+
option_map_or_err_ok::check(cx, expr, recv, def, map);
43384366
},
43394367
("map_or_else", [def, map]) => {
43404368
result_map_or_else_none::check(cx, expr, recv, def, map);
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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_res_lang_ctor, path_res};
5+
use rustc_errors::Applicability;
6+
use rustc_hir::LangItem::{ResultErr, ResultOk};
7+
use rustc_hir::{Expr, ExprKind};
8+
use rustc_lint::LateContext;
9+
use rustc_span::symbol::sym;
10+
11+
use super::OPTION_MAP_OR_ERR_OK;
12+
13+
pub(super) fn check<'tcx>(
14+
cx: &LateContext<'tcx>,
15+
expr: &'tcx Expr<'tcx>,
16+
recv: &'tcx Expr<'_>,
17+
or_expr: &'tcx Expr<'_>,
18+
map_expr: &'tcx Expr<'_>,
19+
) {
20+
// We check that it's called on an `Option` type.
21+
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option)
22+
// We check that first we pass an `Err`.
23+
&& let ExprKind::Call(call, &[arg]) = or_expr.kind
24+
&& is_res_lang_ctor(cx, path_res(cx, call), ResultErr)
25+
// And finally we check that it is mapped as `Ok`.
26+
&& is_res_lang_ctor(cx, path_res(cx, map_expr), ResultOk)
27+
{
28+
let msg = "called `map_or(Err(_), Ok)` on an `Option` value";
29+
let self_snippet = snippet(cx, recv.span, "..");
30+
let err_snippet = snippet(cx, arg.span, "..");
31+
span_lint_and_sugg(
32+
cx,
33+
OPTION_MAP_OR_ERR_OK,
34+
expr.span,
35+
msg,
36+
"try using `ok_or` instead",
37+
format!("{self_snippet}.ok_or({err_snippet})"),
38+
Applicability::MachineApplicable,
39+
);
40+
}
41+
}

0 commit comments

Comments
 (0)