Skip to content

Commit a8f69b2

Browse files
committed
Refactor single_match
1 parent 4350678 commit a8f69b2

File tree

1 file changed

+23
-59
lines changed

1 file changed

+23
-59
lines changed

clippy_lints/src/matches/single_match.rs

Lines changed: 23 additions & 59 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, BindingAnnotation, Block, Expr, ExprKind, Pat, PatKind};
7+
use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, Pat, PatKind};
88
use rustc_lint::LateContext;
99
use rustc_middle::ty::{self, Ty};
1010
use rustc_span::{sym, Span};
@@ -28,67 +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(cx, ex, arms, expr, els);
60-
check_opt_like(cx, ex, arms, expr, ty, 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(
66-
cx: &LateContext<'_>,
67-
ex: &Expr<'_>,
68-
arms: &[Arm<'_>],
69-
expr: &Expr<'_>,
70-
els: Option<&Expr<'_>>,
71-
) {
72-
if is_wild(arms[1].pat) {
73-
report_single_pattern(cx, ex, arms, expr, els);
74-
}
75-
}
76-
77-
fn report_single_pattern(
78-
cx: &LateContext<'_>,
79-
ex: &Expr<'_>,
80-
arms: &[Arm<'_>],
81-
expr: &Expr<'_>,
82-
els: Option<&Expr<'_>>,
83-
) {
62+
fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, expr: &Expr<'_>, els: Option<&Expr<'_>>) {
8463
let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
8564
let ctxt = expr.span.ctxt();
8665
let mut app = Applicability::HasPlaceholders;
8766
let els_str = els.map_or(String::new(), |els| {
8867
format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app))
8968
});
9069

91-
let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
70+
let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat);
9271
let (msg, sugg) = if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind
9372
&& let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex))
9473
&& let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait()
@@ -122,39 +101,24 @@ fn report_single_pattern(
122101
snippet(cx, ex.span, ".."),
123102
// PartialEq for different reference counts may not exist.
124103
"&".repeat(ref_count_diff),
125-
snippet(cx, arms[0].pat.span, ".."),
126-
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),
127106
);
128107
(msg, sugg)
129108
} else {
130109
let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`";
131110
let sugg = format!(
132111
"if let {} = {} {}{els_str}",
133-
snippet(cx, arms[0].pat.span, ".."),
112+
snippet(cx, arm.pat.span, ".."),
134113
snippet(cx, ex.span, ".."),
135-
expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app),
114+
expr_block(cx, arm.body, ctxt, "..", Some(expr.span), &mut app),
136115
);
137116
(msg, sugg)
138117
};
139118

140119
span_lint_and_sugg(cx, lint, expr.span, msg, "try", sugg, app);
141120
}
142121

143-
fn check_opt_like<'a>(
144-
cx: &LateContext<'a>,
145-
ex: &Expr<'_>,
146-
arms: &[Arm<'_>],
147-
expr: &Expr<'_>,
148-
ty: Ty<'a>,
149-
els: Option<&Expr<'_>>,
150-
) {
151-
// We don't want to lint if the second arm contains an enum which could
152-
// have more variants in the future.
153-
if form_exhaustive_matches(cx, ty, arms[0].pat, arms[1].pat) {
154-
report_single_pattern(cx, ex, arms, expr, els);
155-
}
156-
}
157-
158122
/// Returns `true` if all of the types in the pattern are enums which we know
159123
/// won't be expanded in the future
160124
fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) -> bool {

0 commit comments

Comments
 (0)