Skip to content

Commit 3664d63

Browse files
committed
Auto merge of rust-lang#11864 - GuillaumeGomez:option_map_or_err_ok, r=flip1995
Create new lint `option_map_or_err_ok` Fixes rust-lang#10045. For the following code: ```rust let opt = Some(1); opt.map_or(Err("error"), Ok); ``` It suggests to instead write: ```rust let opt = Some(1); opt.ok_or("error"); ``` r? `@flip1995` changelog: Create new lint `option_map_or_err_ok`
2 parents fbf13ce + ef38969 commit 3664d63

8 files changed

+106
-1
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5382,6 +5382,7 @@ Released 2018-09-13
53825382
[`option_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_expect_used
53835383
[`option_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_filter_map
53845384
[`option_if_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
5385+
[`option_map_or_err_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_err_ok
53855386
[`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none
53865387
[`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn
53875388
[`option_map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or

clippy_lints/src/declared_lints.rs

+1
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

+28
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);
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+
}

tests/ui/manual_ok_or.stderr

+10-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ error: this pattern reimplements `Option::ok_or`
1313
LL | foo.map_or(Err("error"), Ok);
1414
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `foo.ok_or("error")`
1515

16+
error: called `map_or(Err(_), Ok)` on an `Option` value
17+
--> $DIR/manual_ok_or.rs:14:5
18+
|
19+
LL | foo.map_or(Err("error"), Ok);
20+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `ok_or` instead: `foo.ok_or("error")`
21+
|
22+
= note: `-D clippy::option-map-or-err-ok` implied by `-D warnings`
23+
= help: to override `-D warnings` add `#[allow(clippy::option_map_or_err_ok)]`
24+
1625
error: this pattern reimplements `Option::ok_or`
1726
--> $DIR/manual_ok_or.rs:17:5
1827
|
@@ -38,5 +47,5 @@ LL + "{}{}{}{}{}{}{}",
3847
LL ~ "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer"));
3948
|
4049

41-
error: aborting due to 4 previous errors
50+
error: aborting due to 5 previous errors
4251

tests/ui/option_map_or_err_ok.fixed

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#![warn(clippy::option_map_or_err_ok)]
2+
3+
fn main() {
4+
let x = Some("a");
5+
let _ = x.ok_or("a");
6+
//~^ ERROR: called `map_or(Err(_), Ok)` on an `Option` value
7+
}

tests/ui/option_map_or_err_ok.rs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#![warn(clippy::option_map_or_err_ok)]
2+
3+
fn main() {
4+
let x = Some("a");
5+
let _ = x.map_or(Err("a"), Ok);
6+
//~^ ERROR: called `map_or(Err(_), Ok)` on an `Option` value
7+
}

tests/ui/option_map_or_err_ok.stderr

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: called `map_or(Err(_), Ok)` on an `Option` value
2+
--> $DIR/option_map_or_err_ok.rs:5:13
3+
|
4+
LL | let _ = x.map_or(Err("a"), Ok);
5+
| ^^^^^^^^^^^^^^^^^^^^^^ help: try using `ok_or` instead: `x.ok_or("a")`
6+
|
7+
= note: `-D clippy::option-map-or-err-ok` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::option_map_or_err_ok)]`
9+
10+
error: aborting due to previous error
11+

0 commit comments

Comments
 (0)