Skip to content

Commit db3fcf8

Browse files
committed
add basic code to check nop match blocks
modify `manual_map_option` uitest because one test case has confliction.
1 parent 30fb822 commit db3fcf8

File tree

7 files changed

+238
-73
lines changed

7 files changed

+238
-73
lines changed

clippy_lints/src/matches/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ mod match_same_arms;
1616
mod match_single_binding;
1717
mod match_wild_enum;
1818
mod match_wild_err_arm;
19+
mod nop_match;
1920
mod overlapping_arms;
2021
mod redundant_pattern_match;
2122
mod rest_pat_in_fully_bound_struct;
2223
mod single_match;
2324
mod wild_in_or_pats;
24-
mod nop_match;
2525

2626
declare_clippy_lint! {
2727
/// ### What it does
@@ -569,7 +569,7 @@ declare_clippy_lint! {
569569

570570
declare_clippy_lint! {
571571
/// ### What it does
572-
/// Checks for unnecessary `match` or match-like `if let` returns for `Option` and `Result`
572+
/// Checks for unnecessary `match` or match-like `if let` returns for `Option` and `Result`
573573
/// when function signatures are the same.
574574
///
575575
/// ### Why is this bad?
@@ -583,7 +583,7 @@ declare_clippy_lint! {
583583
/// Err(err) => Err(err),
584584
/// }
585585
/// }
586-
///
586+
///
587587
/// fn bar() -> Option<i32> {
588588
/// if let Some(val) = option {
589589
/// Some(val)
@@ -594,12 +594,12 @@ declare_clippy_lint! {
594594
/// ```
595595
///
596596
/// Could be replaced as
597-
///
597+
///
598598
/// ```rust,ignore
599599
/// fn foo() -> Result<(), i32> {
600600
/// result
601601
/// }
602-
///
602+
///
603603
/// fn bar() -> Option<i32> {
604604
/// option
605605
/// }

clippy_lints/src/matches/nop_match.rs

Lines changed: 90 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
#![allow(unused_variables)]
2+
use super::NOP_MATCH;
23
use clippy_utils::diagnostics::span_lint_and_sugg;
3-
use rustc_lint::LateContext;
4-
use rustc_hir::{Arm, Expr};
4+
use clippy_utils::source::snippet_with_applicability;
5+
use clippy_utils::{eq_expr_value, get_parent_expr};
56
use rustc_errors::Applicability;
6-
use super::NOP_MATCH;
7+
use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, Pat, PatKind, PathSegment, QPath};
8+
use rustc_lint::LateContext;
79

810
pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) {
911
if false {
@@ -20,15 +22,95 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) {
2022
}
2123

2224
pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
23-
if false {
25+
// This is for avoiding collision with `match_single_binding`.
26+
if arms.len() < 2 {
27+
return;
28+
}
29+
30+
for arm in arms {
31+
if let PatKind::Wild = arm.pat.kind {
32+
let ret_expr = strip_return(arm.body);
33+
if !eq_expr_value(cx, ex, ret_expr) {
34+
return;
35+
}
36+
} else if !pat_same_as_expr(arm.pat, arm.body) {
37+
return;
38+
}
39+
}
40+
41+
if let Some(match_expr) = get_parent_expr(cx, ex) {
42+
let mut applicability = Applicability::MachineApplicable;
2443
span_lint_and_sugg(
2544
cx,
2645
NOP_MATCH,
27-
ex.span,
46+
match_expr.span,
2847
"this match expression is unnecessary",
2948
"replace it with",
30-
"".to_string(),
31-
Applicability::MachineApplicable,
49+
snippet_with_applicability(cx, ex.span, "..", &mut applicability).to_string(),
50+
applicability,
3251
);
3352
}
34-
}
53+
}
54+
55+
fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
56+
if let ExprKind::Ret(Some(ret)) = expr.kind {
57+
ret
58+
} else {
59+
expr
60+
}
61+
}
62+
63+
fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
64+
let expr = strip_return(expr);
65+
match (&pat.kind, &expr.kind) {
66+
(
67+
PatKind::TupleStruct(QPath::Resolved(_, path), [first_pat, ..], _),
68+
ExprKind::Call(call_expr, [first_param, ..]),
69+
) => {
70+
if let ExprKind::Path(QPath::Resolved(_, call_path)) = call_expr.kind {
71+
if is_identical_segments(path.segments, call_path.segments)
72+
&& has_same_non_ref_symbol(first_pat, first_param)
73+
{
74+
return true;
75+
}
76+
}
77+
},
78+
(PatKind::Path(QPath::Resolved(_, p_path)), ExprKind::Path(QPath::Resolved(_, e_path))) => {
79+
return is_identical_segments(p_path.segments, e_path.segments);
80+
},
81+
(PatKind::Lit(pat_lit_expr), ExprKind::Lit(expr_spanned)) => {
82+
if let ExprKind::Lit(pat_spanned) = &pat_lit_expr.kind {
83+
return pat_spanned.node == expr_spanned.node;
84+
}
85+
},
86+
_ => {},
87+
}
88+
89+
false
90+
}
91+
92+
fn is_identical_segments(left_segs: &[PathSegment<'_>], right_segs: &[PathSegment<'_>]) -> bool {
93+
if left_segs.len() != right_segs.len() {
94+
return false;
95+
}
96+
for i in 0..left_segs.len() {
97+
if left_segs[i].ident.name != right_segs[i].ident.name {
98+
return false;
99+
}
100+
}
101+
true
102+
}
103+
104+
fn has_same_non_ref_symbol(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
105+
if_chain! {
106+
if let PatKind::Binding(annot, _, pat_ident, _) = pat.kind;
107+
if !matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut);
108+
if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind;
109+
if let Some(first_seg) = path.segments.first();
110+
then {
111+
return pat_ident.name == first_seg.ident.name;
112+
}
113+
}
114+
115+
false
116+
}

tests/ui/manual_map_option.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ fn main() {
148148

149149
// #7077
150150
let s = &String::new();
151+
#[allow(clippy::nop_match)]
151152
let _: Option<&str> = match Some(s) {
152153
Some(s) => Some(s),
153154
None => None,

tests/ui/manual_map_option.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ fn main() {
214214

215215
// #7077
216216
let s = &String::new();
217+
#[allow(clippy::nop_match)]
217218
let _: Option<&str> = match Some(s) {
218219
Some(s) => Some(s),
219220
None => None,

tests/ui/nop_match.fixed

Lines changed: 38 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,47 @@
44
#![allow(clippy::question_mark)]
55
#![allow(dead_code)]
66

7-
fn option_match() -> Option<i32> {
8-
match Some(1) {
9-
Some(a) => Some(a),
10-
None => None
7+
fn func_ret_err<T>(err: T) -> Result<(), T> {
8+
Err(err)
9+
}
10+
11+
enum SampleEnum {
12+
A,
13+
B,
14+
C,
15+
}
16+
17+
fn useless_prim_type_match(x: i32) -> i32 {
18+
x
19+
}
20+
21+
fn useless_custom_type_match(se: SampleEnum) -> SampleEnum {
22+
se
23+
}
24+
25+
// Don't trigger
26+
fn mingled_custom_type(se: SampleEnum) -> SampleEnum {
27+
match se {
28+
SampleEnum::A => SampleEnum::B,
29+
SampleEnum::B => SampleEnum::C,
30+
SampleEnum::C => SampleEnum::A,
1131
}
1232
}
1333

34+
fn option_match() -> Option<i32> {
35+
Some(1)
36+
}
37+
1438
fn result_match() -> Result<i32, i32> {
15-
match Ok(1) {
16-
Ok(a) => Ok(a),
17-
Err(err) => Err(err)
18-
}
39+
Ok(1)
40+
}
41+
42+
fn result_match_func_call() {
43+
let _ = func_ret_err(0_i32);
1944
}
2045

2146
fn option_check() -> Option<i32> {
22-
if let Some(a) = Some(1) {
23-
Some(a)
24-
} else {
25-
None
26-
}
47+
if let Some(a) = Some(1) { Some(a) } else { None }
2748
}
2849

2950
fn option_check_no_else() -> Option<i32> {
@@ -33,10 +54,6 @@ fn option_check_no_else() -> Option<i32> {
3354
None
3455
}
3556

36-
fn func_ret_err<T>(err: T) -> Result<(), T> {
37-
Err(err)
38-
}
39-
4057
fn result_check_no_else() -> Result<(), i32> {
4158
if let Err(e) = func_ret_err(0_i32) {
4259
return Err(e);
@@ -54,30 +71,18 @@ fn result_check_a() -> Result<(), i32> {
5471

5572
// Don't trigger
5673
fn result_check_b() -> Result<(), i32> {
57-
if let Err(e) = Ok(1) {
58-
Err(e)
59-
} else {
60-
Ok(())
61-
}
74+
if let Err(e) = Ok(1) { Err(e) } else { Ok(()) }
6275
}
6376

6477
fn result_check_c() -> Result<(), i32> {
6578
let example = Ok(());
66-
if let Err(e) = example {
67-
Err(e)
68-
} else {
69-
example
70-
}
79+
if let Err(e) = example { Err(e) } else { example }
7180
}
7281

7382
// Don't trigger
7483
fn result_check_d() -> Result<(), i32> {
7584
let example = Ok(1);
76-
if let Err(e) = example {
77-
Err(e)
78-
} else {
79-
Ok(())
80-
}
85+
if let Err(e) = example { Err(e) } else { Ok(()) }
8186
}
8287

83-
fn main() { }
88+
fn main() {}

0 commit comments

Comments
 (0)