Skip to content

Commit

Permalink
Create new lint option_map_or_err_ok
Browse files Browse the repository at this point in the history
  • Loading branch information
GuillaumeGomez committed Nov 24, 2023
1 parent 96eab06 commit b86903d
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 0 deletions.
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::methods::OK_EXPECT_INFO,
crate::methods::OPTION_AS_REF_DEREF_INFO,
crate::methods::OPTION_FILTER_MAP_INFO,
crate::methods::OPTION_MAP_OR_ERR_OK_INFO,
crate::methods::OPTION_MAP_OR_NONE_INFO,
crate::methods::OR_FUN_CALL_INFO,
crate::methods::OR_THEN_UNWRAP_INFO,
Expand Down
28 changes: 28 additions & 0 deletions clippy_lints/src/methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ mod obfuscated_if_else;
mod ok_expect;
mod open_options;
mod option_as_ref_deref;
mod option_map_or_err_ok;
mod option_map_or_none;
mod option_map_unwrap_or;
mod or_fun_call;
Expand Down Expand Up @@ -3726,6 +3727,31 @@ declare_clippy_lint! {
"calls to `Path::join` which will overwrite the original path"
}

declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `_.map_or(Err(), Ok)`.
///
/// ### Why is this bad?
/// Readability, this can be written more concisely as
/// `_.ok()`.
///
/// ### Example
/// ```no_run
/// # let opt = Some(1);
/// opt.map_or(Err("error"), Ok);
/// ```
///
/// Use instead:
/// ```no_run
/// # let opt = Some(1);
/// opt.ok_or(Err("error"));
/// ```
#[clippy::version = "1.76.0"]
pub OPTION_MAP_OR_ERR_OK,
style,
"using `Option.map_or(Err(), Ok)`, which is more succinctly expressed as `Option.ok()`"
}

pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Msrv,
Expand Down Expand Up @@ -3876,6 +3902,7 @@ impl_lint_pass!(Methods => [
WAKER_CLONE_WAKE,
UNNECESSARY_FALLIBLE_CONVERSIONS,
JOIN_ABSOLUTE_PATHS,
OPTION_MAP_OR_ERR_OK,
]);

/// Extracts a method call name, args, and `Span` of the method name.
Expand Down Expand Up @@ -4335,6 +4362,7 @@ impl Methods {
("map_or", [def, map]) => {
option_map_or_none::check(cx, expr, recv, def, map);
manual_ok_or::check(cx, expr, recv, def, map);
option_map_or_err_ok::check(cx, expr, recv, def, map);
},
("map_or_else", [def, map]) => {
result_map_or_else_none::check(cx, expr, recv, def, map);
Expand Down
45 changes: 45 additions & 0 deletions clippy_lints/src/methods/option_map_or_err_ok.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{is_res_lang_ctor, path_res};
use rustc_errors::Applicability;
use rustc_hir::LangItem::{ResultErr, ResultOk};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;

use super::OPTION_MAP_OR_ERR_OK;

pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
recv: &'tcx Expr<'_>,
or_expr: &'tcx Expr<'_>,
map_expr: &'tcx Expr<'_>,
) {
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option);

if !is_option {
return;
}

let map_arg_is_ok = is_res_lang_ctor(cx, path_res(cx, map_expr), ResultOk);

if map_arg_is_ok
&& let ExprKind::Call(call, &[arg]) = or_expr.kind
&& is_res_lang_ctor(cx, path_res(cx, call), ResultErr)
{
let msg = "called `map_or(Err(_), Ok)` on an `Option` value";
let self_snippet = snippet(cx, recv.span, "..");
let err_snippet = snippet(cx, arg.span, "..");
span_lint_and_sugg(
cx,
OPTION_MAP_OR_ERR_OK,
expr.span,
msg,
"try using `ok_or` instead",
format!("{self_snippet}.ok_or({err_snippet})"),
Applicability::MachineApplicable,
);
}
}

0 comments on commit b86903d

Please sign in to comment.