Skip to content

Commit bd7226d

Browse files
committed
prelim lint for unnecessary pattern matching
1 parent 34b7d15 commit bd7226d

File tree

5 files changed

+94
-0
lines changed

5 files changed

+94
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5539,6 +5539,7 @@ Released 2018-09-13
55395539
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
55405540
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
55415541
[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings
5542+
[`unnecessary_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_pattern_matching
55425543
[`unnecessary_safety_comment`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_comment
55435544
[`unnecessary_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc
55445545
[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
438438
crate::methods::UNNECESSARY_JOIN_INFO,
439439
crate::methods::UNNECESSARY_LAZY_EVALUATIONS_INFO,
440440
crate::methods::UNNECESSARY_LITERAL_UNWRAP_INFO,
441+
crate::methods::UNNECESSARY_PATTERN_MATCHING_INFO,
441442
crate::methods::UNNECESSARY_SORT_BY_INFO,
442443
crate::methods::UNNECESSARY_TO_OWNED_INFO,
443444
crate::methods::UNWRAP_OR_DEFAULT_INFO,

clippy_lints/src/methods/mod.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ mod unnecessary_iter_cloned;
106106
mod unnecessary_join;
107107
mod unnecessary_lazy_eval;
108108
mod unnecessary_literal_unwrap;
109+
mod unnecessary_pattern_matching;
109110
mod unnecessary_sort_by;
110111
mod unnecessary_to_owned;
111112
mod unwrap_expect_used;
@@ -3683,6 +3684,33 @@ declare_clippy_lint! {
36833684
"calling the `try_from` and `try_into` trait methods when `From`/`Into` is implemented"
36843685
}
36853686

3687+
declare_clippy_lint! {
3688+
/// ### What it does
3689+
/// Converts some constructs mapping an Enum value for equality comparison.
3690+
///
3691+
/// ### Why is this bad?
3692+
/// Calls such as `opt.map_or(false, |val| val == 5)` are needlessly long and cumbersome,
3693+
/// and can be reduced to, for example, `opt == Some(5)` assuming `opt` implements `PartialEq`.
3694+
/// This lint offers readability and conciseness improvements.
3695+
///
3696+
/// ### Example
3697+
/// ```no_run
3698+
/// pub fn a(x: Option<i32>) -> bool {
3699+
/// x.map_or(false, |n| n == 5)
3700+
/// }
3701+
/// ```
3702+
/// Use instead:
3703+
/// ```no_run
3704+
/// pub fn a(x: Option<i32>) -> bool {
3705+
/// x == Some(5)
3706+
/// }
3707+
/// ```
3708+
#[clippy::version = "1.75.0"]
3709+
pub UNNECESSARY_PATTERN_MATCHING,
3710+
style,
3711+
"reduce unnecessary pattern matching for constructs that implement `PartialEq`"
3712+
}
3713+
36863714
pub struct Methods {
36873715
avoid_breaking_exported_api: bool,
36883716
msrv: Msrv,
@@ -3830,6 +3858,7 @@ impl_lint_pass!(Methods => [
38303858
REDUNDANT_AS_STR,
38313859
WAKER_CLONE_WAKE,
38323860
UNNECESSARY_FALLIBLE_CONVERSIONS,
3861+
UNNECESSARY_PATTERN_MATCHING,
38333862
]);
38343863

38353864
/// Extracts a method call name, args, and `Span` of the method name.
@@ -4290,6 +4319,7 @@ impl Methods {
42904319
("map_or", [def, map]) => {
42914320
option_map_or_none::check(cx, expr, recv, def, map);
42924321
manual_ok_or::check(cx, expr, recv, def, map);
4322+
unnecessary_pattern_matching::check(cx, expr, recv, def, map);
42934323
},
42944324
("next", []) => {
42954325
if let Some((name2, recv2, args2, _, _)) = method_call(recv) {
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::path_to_local_id;
3+
use clippy_utils::source::snippet;
4+
use clippy_utils::ty::is_type_diagnostic_item;
5+
use rustc_ast::LitKind::Bool;
6+
use rustc_errors::Applicability;
7+
use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind};
8+
use rustc_lint::LateContext;
9+
use rustc_span::sym;
10+
11+
use super::UNNECESSARY_PATTERN_MATCHING;
12+
13+
// Only checking map_or for now
14+
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def: &Expr<'_>, map: &Expr<'_>) {
15+
if let ExprKind::Lit(def_kind) = def.kind
16+
// only works on Result and Option
17+
&& let recv_ty = cx.typeck_results().expr_ty(recv)
18+
&& (is_type_diagnostic_item(cx, recv_ty, sym::Option)
19+
|| is_type_diagnostic_item(cx, recv_ty, sym::Result))
20+
// check we are dealing with boolean map_or
21+
&& let Bool(def_bool) = def_kind.node
22+
&& def_bool == false
23+
&& let ExprKind::Closure(map_closure) = map.kind
24+
&& let closure_body = cx.tcx.hir().body(map_closure.body)
25+
&& let closure_body_value = closure_body.value.peel_blocks()
26+
// check we are dealing with equality statement in the closure
27+
&& let ExprKind::Binary(op, l, r) = closure_body_value.kind
28+
&& let Some(param) = closure_body.params.first()
29+
&& let PatKind::Binding(_, hir_id, _, _) = param.pat.kind
30+
&& BinOpKind::Eq == op.node
31+
&& (path_to_local_id(l, hir_id) || path_to_local_id(r, hir_id))
32+
{
33+
let wrap = if is_type_diagnostic_item(cx, recv_ty, sym::Option) {
34+
"Some"
35+
} else {
36+
"Result"
37+
};
38+
39+
// we already checked either l or r is the local id earlier
40+
let non_binding_location = if path_to_local_id(l, hir_id) { r } else { l };
41+
42+
span_lint_and_sugg(
43+
cx,
44+
UNNECESSARY_PATTERN_MATCHING,
45+
expr.span,
46+
"`map_or` here is redundant, use typical equality instead",
47+
"try",
48+
format!(
49+
"{} == {}({})",
50+
snippet(cx, recv.span, ".."),
51+
wrap,
52+
snippet(cx, non_binding_location.span.source_callsite(), "..")
53+
),
54+
Applicability::Unspecified,
55+
)
56+
}
57+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#![warn(clippy::unnecessary_pattern_matching)]
2+
3+
fn main() {
4+
// test code goes here
5+
}

0 commit comments

Comments
 (0)