Skip to content

Commit 514824f

Browse files
committed
Refactor single_match
1 parent 5ead90f commit 514824f

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
@@ -6,7 +6,7 @@ use clippy_utils::{
66
};
77
use core::cmp::max;
88
use rustc_errors::Applicability;
9-
use rustc_hir::{Arm, BindingMode, Block, Expr, ExprKind, Pat, PatKind};
9+
use rustc_hir::{Arm, BindingMode, Expr, ExprKind, Pat, PatKind};
1010
use rustc_lint::LateContext;
1111
use rustc_middle::ty::{self, Ty};
1212
use rustc_span::{sym, Span};
@@ -30,59 +30,46 @@ fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool {
3030

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

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

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

85-
let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
72+
let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat);
8673
let (msg, sugg) = if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind
8774
&& let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex))
8875
&& let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait()
@@ -116,30 +103,24 @@ fn report_single_pattern(
116103
snippet(cx, ex.span, ".."),
117104
// PartialEq for different reference counts may not exist.
118105
"&".repeat(ref_count_diff),
119-
snippet(cx, arms[0].pat.span, ".."),
120-
expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app),
106+
snippet(cx, arm.pat.span, ".."),
107+
expr_block(cx, arm.body, ctxt, "..", Some(expr.span), &mut app),
121108
);
122109
(msg, sugg)
123110
} else {
124111
let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`";
125112
let sugg = format!(
126113
"if let {} = {} {}{els_str}",
127-
snippet(cx, arms[0].pat.span, ".."),
114+
snippet(cx, arm.pat.span, ".."),
128115
snippet(cx, ex.span, ".."),
129-
expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app),
116+
expr_block(cx, arm.body, ctxt, "..", Some(expr.span), &mut app),
130117
);
131118
(msg, sugg)
132119
};
133120

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

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

0 commit comments

Comments
 (0)