Skip to content

Commit 9bfcbf4

Browse files
committed
Remove some redundant checks in various matches lints
1 parent a4cf91b commit 9bfcbf4

File tree

4 files changed

+128
-129
lines changed

4 files changed

+128
-129
lines changed

clippy_lints/src/matches/match_like_matches.rs

+24-21
Original file line numberDiff line numberDiff line change
@@ -3,49 +3,52 @@ use clippy_utils::source::snippet_with_applicability;
33
use clippy_utils::{higher, is_wild};
44
use rustc_ast::{Attribute, LitKind};
55
use rustc_errors::Applicability;
6-
use rustc_hir::{BorrowKind, Expr, ExprKind, Guard, MatchSource, Pat};
6+
use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat};
77
use rustc_lint::LateContext;
88
use rustc_middle::ty;
99
use rustc_span::source_map::Spanned;
1010

1111
use super::MATCH_LIKE_MATCHES_MACRO;
1212

1313
/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
14-
pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
14+
pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
1515
if let Some(higher::IfLet {
1616
let_pat,
1717
let_expr,
1818
if_then,
1919
if_else: Some(if_else),
2020
}) = higher::IfLet::hir(cx, expr)
2121
{
22-
return find_matches_sugg(
22+
find_matches_sugg(
2323
cx,
2424
let_expr,
2525
IntoIterator::into_iter([(&[][..], Some(let_pat), if_then, None), (&[][..], None, if_else, None)]),
2626
expr,
2727
true,
2828
);
2929
}
30+
}
3031

31-
if let ExprKind::Match(scrut, arms, MatchSource::Normal) = expr.kind {
32-
return find_matches_sugg(
33-
cx,
34-
scrut,
35-
arms.iter().map(|arm| {
36-
(
37-
cx.tcx.hir().attrs(arm.hir_id),
38-
Some(arm.pat),
39-
arm.body,
40-
arm.guard.as_ref(),
41-
)
42-
}),
43-
expr,
44-
false,
45-
);
46-
}
47-
48-
false
32+
pub(super) fn check_match<'tcx>(
33+
cx: &LateContext<'tcx>,
34+
e: &'tcx Expr<'_>,
35+
scrutinee: &'tcx Expr<'_>,
36+
arms: &'tcx [Arm<'tcx>],
37+
) -> bool {
38+
find_matches_sugg(
39+
cx,
40+
scrutinee,
41+
arms.iter().map(|arm| {
42+
(
43+
cx.tcx.hir().attrs(arm.hir_id),
44+
Some(arm.pat),
45+
arm.body,
46+
arm.guard.as_ref(),
47+
)
48+
}),
49+
e,
50+
false,
51+
)
4952
}
5053

5154
/// Lint a `match` or `if let` for replacement by `matches!`

clippy_lints/src/matches/match_same_arms.rs

+75-77
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,94 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::source::snippet;
33
use clippy_utils::{path_to_local, search_same, SpanlessEq, SpanlessHash};
4-
use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdSet, MatchSource, Pat, PatKind};
4+
use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdSet, Pat, PatKind};
55
use rustc_lint::LateContext;
66
use std::collections::hash_map::Entry;
77

88
use super::MATCH_SAME_ARMS;
99

10-
pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
11-
if let ExprKind::Match(_, arms, MatchSource::Normal) = expr.kind {
12-
let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
13-
let mut h = SpanlessHash::new(cx);
14-
h.hash_expr(arm.body);
15-
h.finish()
16-
};
10+
pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
11+
let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
12+
let mut h = SpanlessHash::new(cx);
13+
h.hash_expr(arm.body);
14+
h.finish()
15+
};
1716

18-
let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool {
19-
let min_index = usize::min(lindex, rindex);
20-
let max_index = usize::max(lindex, rindex);
17+
let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool {
18+
let min_index = usize::min(lindex, rindex);
19+
let max_index = usize::max(lindex, rindex);
2120

22-
let mut local_map: HirIdMap<HirId> = HirIdMap::default();
23-
let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
24-
if_chain! {
25-
if let Some(a_id) = path_to_local(a);
26-
if let Some(b_id) = path_to_local(b);
27-
let entry = match local_map.entry(a_id) {
28-
Entry::Vacant(entry) => entry,
29-
// check if using the same bindings as before
30-
Entry::Occupied(entry) => return *entry.get() == b_id,
31-
};
32-
// the names technically don't have to match; this makes the lint more conservative
33-
if cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id);
34-
if cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b);
35-
if pat_contains_local(lhs.pat, a_id);
36-
if pat_contains_local(rhs.pat, b_id);
37-
then {
38-
entry.insert(b_id);
39-
true
40-
} else {
41-
false
42-
}
21+
let mut local_map: HirIdMap<HirId> = HirIdMap::default();
22+
let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
23+
if_chain! {
24+
if let Some(a_id) = path_to_local(a);
25+
if let Some(b_id) = path_to_local(b);
26+
let entry = match local_map.entry(a_id) {
27+
Entry::Vacant(entry) => entry,
28+
// check if using the same bindings as before
29+
Entry::Occupied(entry) => return *entry.get() == b_id,
30+
};
31+
// the names technically don't have to match; this makes the lint more conservative
32+
if cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id);
33+
if cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b);
34+
if pat_contains_local(lhs.pat, a_id);
35+
if pat_contains_local(rhs.pat, b_id);
36+
then {
37+
entry.insert(b_id);
38+
true
39+
} else {
40+
false
4341
}
44-
};
45-
// Arms with a guard are ignored, those can’t always be merged together
46-
// This is also the case for arms in-between each there is an arm with a guard
47-
(min_index..=max_index).all(|index| arms[index].guard.is_none())
48-
&& SpanlessEq::new(cx)
49-
.expr_fallback(eq_fallback)
50-
.eq_expr(lhs.body, rhs.body)
51-
// these checks could be removed to allow unused bindings
52-
&& bindings_eq(lhs.pat, local_map.keys().copied().collect())
53-
&& bindings_eq(rhs.pat, local_map.values().copied().collect())
42+
}
5443
};
44+
// Arms with a guard are ignored, those can’t always be merged together
45+
// This is also the case for arms in-between each there is an arm with a guard
46+
(min_index..=max_index).all(|index| arms[index].guard.is_none())
47+
&& SpanlessEq::new(cx)
48+
.expr_fallback(eq_fallback)
49+
.eq_expr(lhs.body, rhs.body)
50+
// these checks could be removed to allow unused bindings
51+
&& bindings_eq(lhs.pat, local_map.keys().copied().collect())
52+
&& bindings_eq(rhs.pat, local_map.values().copied().collect())
53+
};
5554

56-
let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
57-
for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) {
58-
span_lint_and_then(
59-
cx,
60-
MATCH_SAME_ARMS,
61-
j.body.span,
62-
"this `match` has identical arm bodies",
63-
|diag| {
64-
diag.span_note(i.body.span, "same as this");
55+
let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
56+
for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) {
57+
span_lint_and_then(
58+
cx,
59+
MATCH_SAME_ARMS,
60+
j.body.span,
61+
"this `match` has identical arm bodies",
62+
|diag| {
63+
diag.span_note(i.body.span, "same as this");
6564

66-
// Note: this does not use `span_suggestion` on purpose:
67-
// there is no clean way
68-
// to remove the other arm. Building a span and suggest to replace it to ""
69-
// makes an even more confusing error message. Also in order not to make up a
70-
// span for the whole pattern, the suggestion is only shown when there is only
71-
// one pattern. The user should know about `|` if they are already using it…
65+
// Note: this does not use `span_suggestion` on purpose:
66+
// there is no clean way
67+
// to remove the other arm. Building a span and suggest to replace it to ""
68+
// makes an even more confusing error message. Also in order not to make up a
69+
// span for the whole pattern, the suggestion is only shown when there is only
70+
// one pattern. The user should know about `|` if they are already using it…
7271

73-
let lhs = snippet(cx, i.pat.span, "<pat1>");
74-
let rhs = snippet(cx, j.pat.span, "<pat2>");
72+
let lhs = snippet(cx, i.pat.span, "<pat1>");
73+
let rhs = snippet(cx, j.pat.span, "<pat2>");
7574

76-
if let PatKind::Wild = j.pat.kind {
77-
// if the last arm is _, then i could be integrated into _
78-
// note that i.pat cannot be _, because that would mean that we're
79-
// hiding all the subsequent arms, and rust won't compile
80-
diag.span_note(
81-
i.body.span,
82-
&format!(
83-
"`{}` has the same arm body as the `_` wildcard, consider removing it",
84-
lhs
85-
),
86-
);
87-
} else {
88-
diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs,))
89-
.help("...or consider changing the match arm bodies");
90-
}
91-
},
92-
);
93-
}
75+
if let PatKind::Wild = j.pat.kind {
76+
// if the last arm is _, then i could be integrated into _
77+
// note that i.pat cannot be _, because that would mean that we're
78+
// hiding all the subsequent arms, and rust won't compile
79+
diag.span_note(
80+
i.body.span,
81+
&format!(
82+
"`{}` has the same arm body as the `_` wildcard, consider removing it",
83+
lhs
84+
),
85+
);
86+
} else {
87+
diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs,))
88+
.help("...or consider changing the match arm bodies");
89+
}
90+
},
91+
);
9492
}
9593
}
9694

clippy_lints/src/matches/mod.rs

+25-23
Original file line numberDiff line numberDiff line change
@@ -604,33 +604,35 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
604604
return;
605605
}
606606

607-
redundant_pattern_match::check(cx, expr);
607+
if let ExprKind::Match(ex, arms, source) = expr.kind {
608+
if source == MatchSource::Normal {
609+
if !(meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO)
610+
&& match_like_matches::check_match(cx, expr, ex, arms))
611+
{
612+
match_same_arms::check(cx, arms);
613+
}
608614

609-
if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) {
610-
if !match_like_matches::check(cx, expr) {
611-
match_same_arms::check(cx, expr);
612-
}
613-
} else {
614-
match_same_arms::check(cx, expr);
615-
}
616-
617-
if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind {
618-
single_match::check(cx, ex, arms, expr);
619-
match_bool::check(cx, ex, arms, expr);
620-
overlapping_arms::check(cx, ex, arms);
621-
match_wild_err_arm::check(cx, ex, arms);
622-
match_wild_enum::check(cx, ex, arms);
623-
match_as_ref::check(cx, ex, arms, expr);
624-
wild_in_or_pats::check(cx, arms);
615+
redundant_pattern_match::check_match(cx, expr, ex, arms);
616+
single_match::check(cx, ex, arms, expr);
617+
match_bool::check(cx, ex, arms, expr);
618+
overlapping_arms::check(cx, ex, arms);
619+
match_wild_err_arm::check(cx, ex, arms);
620+
match_wild_enum::check(cx, ex, arms);
621+
match_as_ref::check(cx, ex, arms, expr);
622+
wild_in_or_pats::check(cx, arms);
625623

626-
if self.infallible_destructuring_match_linted {
627-
self.infallible_destructuring_match_linted = false;
628-
} else {
629-
match_single_binding::check(cx, ex, arms, expr);
624+
if self.infallible_destructuring_match_linted {
625+
self.infallible_destructuring_match_linted = false;
626+
} else {
627+
match_single_binding::check(cx, ex, arms, expr);
628+
}
630629
}
631-
}
632-
if let ExprKind::Match(ex, arms, _) = expr.kind {
633630
match_ref_pats::check(cx, ex, arms.iter().map(|el| el.pat), expr);
631+
} else {
632+
if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) {
633+
match_like_matches::check(cx, expr);
634+
}
635+
redundant_pattern_match::check(cx, expr);
634636
}
635637
}
636638

clippy_lints/src/matches/redundant_pattern_match.rs

+4-8
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ use rustc_errors::Applicability;
1212
use rustc_hir::LangItem::{OptionNone, PollPending};
1313
use rustc_hir::{
1414
intravisit::{walk_expr, Visitor},
15-
Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, Pat, PatKind, QPath, UnOp,
15+
Arm, Block, Expr, ExprKind, LangItem, Node, Pat, PatKind, QPath, UnOp,
1616
};
1717
use rustc_lint::LateContext;
1818
use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty};
1919
use rustc_span::sym;
2020

21-
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
21+
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
2222
if let Some(higher::IfLet {
2323
if_else,
2424
let_pat,
@@ -27,11 +27,7 @@ pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
2727
}) = higher::IfLet::hir(cx, expr)
2828
{
2929
find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some());
30-
}
31-
if let ExprKind::Match(op, arms, MatchSource::Normal) = &expr.kind {
32-
find_sugg_for_match(cx, expr, op, arms);
33-
}
34-
if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
30+
} else if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
3531
find_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false);
3632
}
3733
}
@@ -304,7 +300,7 @@ fn find_sugg_for_if_let<'tcx>(
304300
);
305301
}
306302

307-
fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) {
303+
pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) {
308304
if arms.len() == 2 {
309305
let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind);
310306

0 commit comments

Comments
 (0)