Skip to content

Commit 5bc8813

Browse files
committed
Move ManualOkOr into Methods lint pass
1 parent a8d80d5 commit 5bc8813

File tree

6 files changed

+98
-100
lines changed

6 files changed

+98
-100
lines changed

clippy_lints/src/lib.register_lints.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,6 @@ store.register_lints(&[
243243
manual_empty_string_creations::MANUAL_EMPTY_STRING_CREATIONS,
244244
manual_instant_elapsed::MANUAL_INSTANT_ELAPSED,
245245
manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
246-
manual_ok_or::MANUAL_OK_OR,
247246
manual_rem_euclid::MANUAL_REM_EUCLID,
248247
manual_retain::MANUAL_RETAIN,
249248
manual_strip::MANUAL_STRIP,
@@ -322,6 +321,7 @@ store.register_lints(&[
322321
methods::ITER_WITH_DRAIN,
323322
methods::MANUAL_FILTER_MAP,
324323
methods::MANUAL_FIND_MAP,
324+
methods::MANUAL_OK_OR,
325325
methods::MANUAL_SATURATING_ARITHMETIC,
326326
methods::MANUAL_SPLIT_ONCE,
327327
methods::MANUAL_STR_REPEAT,

clippy_lints/src/lib.register_pedantic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
4848
LintId::of(macro_use::MACRO_USE_IMPORTS),
4949
LintId::of(manual_assert::MANUAL_ASSERT),
5050
LintId::of(manual_instant_elapsed::MANUAL_INSTANT_ELAPSED),
51-
LintId::of(manual_ok_or::MANUAL_OK_OR),
5251
LintId::of(matches::MATCH_BOOL),
5352
LintId::of(matches::MATCH_ON_VEC_ITEMS),
5453
LintId::of(matches::MATCH_SAME_ARMS),
@@ -62,6 +61,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
6261
LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
6362
LintId::of(methods::IMPLICIT_CLONE),
6463
LintId::of(methods::INEFFICIENT_TO_STRING),
64+
LintId::of(methods::MANUAL_OK_OR),
6565
LintId::of(methods::MAP_UNWRAP_OR),
6666
LintId::of(methods::NAIVE_BYTECOUNT),
6767
LintId::of(methods::UNNECESSARY_JOIN),

clippy_lints/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,6 @@ mod manual_bits;
270270
mod manual_empty_string_creations;
271271
mod manual_instant_elapsed;
272272
mod manual_non_exhaustive;
273-
mod manual_ok_or;
274273
mod manual_rem_euclid;
275274
mod manual_retain;
276275
mod manual_strip;
@@ -838,7 +837,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
838837
store.register_late_pass(|| Box::new(stable_sort_primitive::StableSortPrimitive));
839838
store.register_late_pass(|| Box::new(repeat_once::RepeatOnce));
840839
store.register_late_pass(|| Box::new(unwrap_in_result::UnwrapInResult));
841-
store.register_late_pass(|| Box::new(manual_ok_or::ManualOkOr));
842840
store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
843841
store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
844842
let disallowed_methods = conf.disallowed_methods.clone();

clippy_lints/src/manual_ok_or.rs

Lines changed: 0 additions & 95 deletions
This file was deleted.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
3+
use clippy_utils::ty::is_type_diagnostic_item;
4+
use clippy_utils::{is_lang_ctor, path_to_local_id};
5+
use if_chain::if_chain;
6+
use rustc_errors::Applicability;
7+
use rustc_hir::LangItem::{ResultErr, ResultOk};
8+
use rustc_hir::{Closure, Expr, ExprKind, PatKind};
9+
use rustc_lint::LateContext;
10+
use rustc_span::symbol::sym;
11+
12+
use super::MANUAL_OK_OR;
13+
14+
pub(super) fn check<'tcx>(
15+
cx: &LateContext<'tcx>,
16+
expr: &'tcx Expr<'tcx>,
17+
recv: &'tcx Expr<'_>,
18+
or_expr: &'tcx Expr<'_>,
19+
map_expr: &'tcx Expr<'_>,
20+
) {
21+
if_chain! {
22+
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
23+
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
24+
if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Option);
25+
if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, [err_arg]) = or_expr.kind;
26+
if is_lang_ctor(cx, err_path, ResultErr);
27+
if is_ok_wrapping(cx, map_expr);
28+
if let Some(recv_snippet) = snippet_opt(cx, recv.span);
29+
if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
30+
if let Some(indent) = indent_of(cx, expr.span);
31+
then {
32+
let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4));
33+
span_lint_and_sugg(
34+
cx,
35+
MANUAL_OK_OR,
36+
expr.span,
37+
"this pattern reimplements `Option::ok_or`",
38+
"replace with",
39+
format!(
40+
"{}.ok_or({})",
41+
recv_snippet,
42+
reindented_err_arg_snippet
43+
),
44+
Applicability::MachineApplicable,
45+
);
46+
}
47+
}
48+
}
49+
50+
fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
51+
if let ExprKind::Path(ref qpath) = map_expr.kind {
52+
if is_lang_ctor(cx, qpath, ResultOk) {
53+
return true;
54+
}
55+
}
56+
if_chain! {
57+
if let ExprKind::Closure(&Closure { body, .. }) = map_expr.kind;
58+
let body = cx.tcx.hir().body(body);
59+
if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind;
60+
if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind;
61+
if is_lang_ctor(cx, ok_path, ResultOk);
62+
then { path_to_local_id(ok_arg, param_id) } else { false }
63+
}
64+
}

clippy_lints/src/methods/mod.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ mod iter_overeager_cloned;
4242
mod iter_skip_next;
4343
mod iter_with_drain;
4444
mod iterator_step_by_zero;
45+
mod manual_ok_or;
4546
mod manual_saturating_arithmetic;
4647
mod manual_str_repeat;
4748
mod map_collect_result_unit;
@@ -2484,6 +2485,32 @@ declare_clippy_lint! {
24842485
"Using `x.get(0)` when `x.first()` is simpler"
24852486
}
24862487

2488+
declare_clippy_lint! {
2489+
/// ### What it does
2490+
///
2491+
/// Finds patterns that reimplement `Option::ok_or`.
2492+
///
2493+
/// ### Why is this bad?
2494+
///
2495+
/// Concise code helps focusing on behavior instead of boilerplate.
2496+
///
2497+
/// ### Examples
2498+
/// ```rust
2499+
/// let foo: Option<i32> = None;
2500+
/// foo.map_or(Err("error"), |v| Ok(v));
2501+
/// ```
2502+
///
2503+
/// Use instead:
2504+
/// ```rust
2505+
/// let foo: Option<i32> = None;
2506+
/// foo.ok_or("error");
2507+
/// ```
2508+
#[clippy::version = "1.49.0"]
2509+
pub MANUAL_OK_OR,
2510+
pedantic,
2511+
"finds patterns that can be encoded more concisely with `Option::ok_or`"
2512+
}
2513+
24872514
pub struct Methods {
24882515
avoid_breaking_exported_api: bool,
24892516
msrv: Option<RustcVersion>,
@@ -2592,6 +2619,7 @@ impl_lint_pass!(Methods => [
25922619
BYTES_COUNT_TO_LEN,
25932620
CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
25942621
GET_FIRST,
2622+
MANUAL_OK_OR,
25952623
]);
25962624

25972625
/// Extracts a method call name, args, and `Span` of the method name.
@@ -2936,7 +2964,10 @@ impl Methods {
29362964
}
29372965
map_identity::check(cx, expr, recv, m_arg, name, span);
29382966
},
2939-
("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
2967+
("map_or", [def, map]) => {
2968+
option_map_or_none::check(cx, expr, recv, def, map);
2969+
manual_ok_or::check(cx, expr, recv, def, map);
2970+
},
29402971
("next", []) => {
29412972
if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) {
29422973
match (name2, args2) {

0 commit comments

Comments
 (0)