Skip to content

Commit 11a3aa2

Browse files
committed
Refactor single_match
1 parent 9628130 commit 11a3aa2

File tree

1 file changed

+23
-42
lines changed

1 file changed

+23
-42
lines changed

clippy_lints/src/matches/single_match.rs

Lines changed: 23 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, peel_mid_ty_re
44
use clippy_utils::{is_lint_allowed, is_unit_expr, is_wild, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs};
55
use core::cmp::max;
66
use rustc_errors::Applicability;
7-
use rustc_hir::{Arm, BindingMode, Block, Expr, ExprKind, Pat, PatKind};
7+
use rustc_hir::{Arm, BindingMode, Expr, ExprKind, Pat, PatKind};
88
use rustc_lint::LateContext;
99
use rustc_middle::ty::{self, Ty};
1010
use rustc_span::{sym, Span};
@@ -28,59 +28,46 @@ fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool {
2828

2929
#[rustfmt::skip]
3030
pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
31-
if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
32-
if expr.span.from_expansion() {
33-
// Don't lint match expressions present in
34-
// macro_rules! block
35-
return;
36-
}
37-
if let PatKind::Or(..) = arms[0].pat.kind {
38-
// don't lint for or patterns for now, this makes
39-
// the lint noisy in unnecessary situations
40-
return;
41-
}
42-
let els = arms[1].body;
43-
let els = if is_unit_expr(peel_blocks(els)) && !empty_arm_has_comment(cx, els.span) {
31+
if let [arm1, arm2] = arms
32+
&& arm1.guard.is_none()
33+
&& arm2.guard.is_none()
34+
&& !expr.span.from_expansion()
35+
// don't lint for or patterns for now, this makes
36+
// the lint noisy in unnecessary situations
37+
&& !matches!(arm1.pat.kind, PatKind::Or(..))
38+
{
39+
let els = if is_unit_expr(peel_blocks(arm2.body)) && !empty_arm_has_comment(cx, arm2.body.span) {
4440
None
45-
} else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind {
46-
if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() {
41+
} else if let ExprKind::Block(block, _) = arm2.body.kind {
42+
if matches!((block.stmts, block.expr), ([], Some(_)) | ([_], None)) {
4743
// single statement/expr "else" block, don't lint
4844
return;
4945
}
5046
// block with 2+ statements or 1 expr and 1+ statement
51-
Some(els)
47+
Some(arm2.body)
5248
} else {
5349
// not a block or an empty block w/ comments, don't lint
5450
return;
5551
};
5652

5753
let ty = cx.typeck_results().expr_ty(ex);
58-
if (*ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id)) &&
59-
(check_single_pattern(arms) || check_opt_like(cx, arms, ty)) {
60-
report_single_pattern(cx, ex, arms, expr, els);
54+
if (*ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id))
55+
&& (is_wild(arm2.pat) || form_exhaustive_matches(cx, ty, arm1.pat, arm2.pat))
56+
{
57+
report_single_pattern(cx, ex, arm1, expr, els);
6158
}
6259
}
6360
}
6461

65-
fn check_single_pattern(arms: &[Arm<'_>]) -> bool {
66-
is_wild(arms[1].pat)
67-
}
68-
69-
fn report_single_pattern(
70-
cx: &LateContext<'_>,
71-
ex: &Expr<'_>,
72-
arms: &[Arm<'_>],
73-
expr: &Expr<'_>,
74-
els: Option<&Expr<'_>>,
75-
) {
62+
fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, expr: &Expr<'_>, els: Option<&Expr<'_>>) {
7663
let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
7764
let ctxt = expr.span.ctxt();
7865
let mut app = Applicability::MachineApplicable;
7966
let els_str = els.map_or(String::new(), |els| {
8067
format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app))
8168
});
8269

83-
let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
70+
let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat);
8471
let (msg, sugg) = if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind
8572
&& let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex))
8673
&& let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait()
@@ -114,30 +101,24 @@ fn report_single_pattern(
114101
snippet(cx, ex.span, ".."),
115102
// PartialEq for different reference counts may not exist.
116103
"&".repeat(ref_count_diff),
117-
snippet(cx, arms[0].pat.span, ".."),
118-
expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app),
104+
snippet(cx, arm.pat.span, ".."),
105+
expr_block(cx, arm.body, ctxt, "..", Some(expr.span), &mut app),
119106
);
120107
(msg, sugg)
121108
} else {
122109
let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`";
123110
let sugg = format!(
124111
"if let {} = {} {}{els_str}",
125-
snippet(cx, arms[0].pat.span, ".."),
112+
snippet(cx, arm.pat.span, ".."),
126113
snippet(cx, ex.span, ".."),
127-
expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app),
114+
expr_block(cx, arm.body, ctxt, "..", Some(expr.span), &mut app),
128115
);
129116
(msg, sugg)
130117
};
131118

132119
span_lint_and_sugg(cx, lint, expr.span, msg, "try", sugg, app);
133120
}
134121

135-
fn check_opt_like<'a>(cx: &LateContext<'a>, arms: &[Arm<'_>], ty: Ty<'a>) -> bool {
136-
// We don't want to lint if the second arm contains an enum which could
137-
// have more variants in the future.
138-
form_exhaustive_matches(cx, ty, arms[0].pat, arms[1].pat)
139-
}
140-
141122
/// Returns `true` if all of the types in the pattern are enums which we know
142123
/// won't be expanded in the future
143124
fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) -> bool {

0 commit comments

Comments
 (0)