Skip to content

Commit 20b085d

Browse files
committed
Auto merge of rust-lang#12745 - y21:collapsible_match_or_pat, r=llogiq
Suggest collapsing nested or patterns if the MSRV allows it Nested `or` patterns have been stable since 1.53, so we should be able to suggest `Some(1 | 2)` if the MSRV isn't set below that. This change adds an msrv check and also moves it to `matches/mod.rs`, because it's also needed by `redundant_guards`. changelog: [`collapsible_match`]: suggest collapsing nested or patterns if the MSRV allows it
2 parents c369183 + 790fb93 commit 20b085d

8 files changed

+134
-81
lines changed

clippy_lints/src/matches/collapsible_match.rs

+8-15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use clippy_config::msrvs::Msrv;
12
use clippy_utils::diagnostics::span_lint_and_then;
23
use clippy_utils::higher::IfLetOrMatch;
34
use clippy_utils::source::snippet;
@@ -11,12 +12,12 @@ use rustc_hir::{Arm, Expr, HirId, Pat, PatKind};
1112
use rustc_lint::LateContext;
1213
use rustc_span::Span;
1314

14-
use super::COLLAPSIBLE_MATCH;
15+
use super::{pat_contains_disallowed_or, COLLAPSIBLE_MATCH};
1516

16-
pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
17+
pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], msrv: &Msrv) {
1718
if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) {
1819
for arm in arms {
19-
check_arm(cx, true, arm.pat, arm.body, arm.guard, Some(els_arm.body));
20+
check_arm(cx, true, arm.pat, arm.body, arm.guard, Some(els_arm.body), msrv);
2021
}
2122
}
2223
}
@@ -26,8 +27,9 @@ pub(super) fn check_if_let<'tcx>(
2627
pat: &'tcx Pat<'_>,
2728
body: &'tcx Expr<'_>,
2829
else_expr: Option<&'tcx Expr<'_>>,
30+
msrv: &Msrv,
2931
) {
30-
check_arm(cx, false, pat, body, None, else_expr);
32+
check_arm(cx, false, pat, body, None, else_expr, msrv);
3133
}
3234

3335
fn check_arm<'tcx>(
@@ -37,6 +39,7 @@ fn check_arm<'tcx>(
3739
outer_then_body: &'tcx Expr<'tcx>,
3840
outer_guard: Option<&'tcx Expr<'tcx>>,
3941
outer_else_body: Option<&'tcx Expr<'tcx>>,
42+
msrv: &Msrv,
4043
) {
4144
let inner_expr = peel_blocks_with_stmt(outer_then_body);
4245
if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr)
@@ -57,7 +60,7 @@ fn check_arm<'tcx>(
5760
// match expression must be a local binding
5861
// match <local> { .. }
5962
&& let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee))
60-
&& !pat_contains_or(inner_then_pat)
63+
&& !pat_contains_disallowed_or(inner_then_pat, msrv)
6164
// the binding must come from the pattern of the containing match arm
6265
// ..<local>.. => match <local> { .. }
6366
&& let (Some(binding_span), is_innermost_parent_pat_struct)
@@ -142,13 +145,3 @@ fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: Hi
142145
});
143146
(span, is_innermost_parent_pat_struct)
144147
}
145-
146-
fn pat_contains_or(pat: &Pat<'_>) -> bool {
147-
let mut result = false;
148-
pat.walk(|p| {
149-
let is_or = matches!(p.kind, PatKind::Or(_));
150-
result |= is_or;
151-
!is_or
152-
});
153-
result
154-
}

clippy_lints/src/matches/mod.rs

+19-4
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ mod wild_in_or_pats;
2727
use clippy_config::msrvs::{self, Msrv};
2828
use clippy_utils::source::walk_span_to_context;
2929
use clippy_utils::{higher, in_constant, is_direct_expn_of, is_span_match, span_contains_cfg};
30-
use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat};
30+
use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind};
3131
use rustc_lint::{LateContext, LateLintPass, LintContext};
3232
use rustc_middle::lint::in_external_macro;
3333
use rustc_session::impl_lint_pass;
@@ -1040,7 +1040,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
10401040
significant_drop_in_scrutinee::check(cx, expr, ex, arms, source);
10411041
}
10421042

1043-
collapsible_match::check_match(cx, arms);
1043+
collapsible_match::check_match(cx, arms, &self.msrv);
10441044
if !from_expansion {
10451045
// These don't depend on a relationship between multiple arms
10461046
match_wild_err_arm::check(cx, ex, arms);
@@ -1066,7 +1066,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
10661066
needless_match::check_match(cx, ex, arms, expr);
10671067
match_on_vec_items::check(cx, ex);
10681068
match_str_case_mismatch::check(cx, ex, arms);
1069-
redundant_guards::check(cx, arms);
1069+
redundant_guards::check(cx, arms, &self.msrv);
10701070

10711071
if !in_constant(cx, expr.hir_id) {
10721072
manual_unwrap_or::check(cx, expr, ex, arms);
@@ -1083,7 +1083,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
10831083
match_ref_pats::check(cx, ex, arms.iter().map(|el| el.pat), expr);
10841084
}
10851085
} else if let Some(if_let) = higher::IfLet::hir(cx, expr) {
1086-
collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else);
1086+
collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else, &self.msrv);
10871087
if !from_expansion {
10881088
if let Some(else_expr) = if_let.if_else {
10891089
if self.msrv.meets(msrvs::MATCHES_MACRO) {
@@ -1195,3 +1195,18 @@ fn contains_cfg_arm(cx: &LateContext<'_>, e: &Expr<'_>, scrutinee: &Expr<'_>, ar
11951195
Err(()) => true,
11961196
}
11971197
}
1198+
1199+
/// Checks if `pat` contains OR patterns that cannot be nested due to a too low MSRV.
1200+
fn pat_contains_disallowed_or(pat: &Pat<'_>, msrv: &Msrv) -> bool {
1201+
if msrv.meets(msrvs::OR_PATTERNS) {
1202+
return false;
1203+
}
1204+
1205+
let mut result = false;
1206+
pat.walk(|p| {
1207+
let is_or = matches!(p.kind, PatKind::Or(_));
1208+
result |= is_or;
1209+
!is_or
1210+
});
1211+
result
1212+
}

clippy_lints/src/matches/redundant_guards.rs

+10-17
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,32 @@
1+
use clippy_config::msrvs::Msrv;
12
use clippy_utils::diagnostics::span_lint_and_then;
3+
use clippy_utils::macros::matching_root_macro_call;
24
use clippy_utils::source::snippet;
35
use clippy_utils::visitors::{for_each_expr, is_local_used};
46
use clippy_utils::{in_constant, path_to_local};
57
use rustc_ast::{BorrowKind, LitKind};
68
use rustc_errors::Applicability;
79
use rustc_hir::def::{DefKind, Res};
8-
use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, MatchSource, Node, Pat, PatKind, UnOp};
10+
use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, MatchSource, Node, PatKind, UnOp};
911
use rustc_lint::LateContext;
1012
use rustc_span::symbol::Ident;
11-
use rustc_span::{Span, Symbol};
13+
use rustc_span::{sym, Span, Symbol};
1214
use std::borrow::Cow;
1315
use std::ops::ControlFlow;
1416

15-
use super::REDUNDANT_GUARDS;
17+
use super::{pat_contains_disallowed_or, REDUNDANT_GUARDS};
1618

17-
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
19+
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv: &Msrv) {
1820
for outer_arm in arms {
1921
let Some(guard) = outer_arm.guard else {
2022
continue;
2123
};
2224

2325
// `Some(x) if matches!(x, y)`
24-
if let ExprKind::Match(
25-
scrutinee,
26-
[
27-
arm,
28-
Arm {
29-
pat: Pat {
30-
kind: PatKind::Wild, ..
31-
},
32-
..
33-
},
34-
],
35-
MatchSource::Normal,
36-
) = guard.kind
26+
if let ExprKind::Match(scrutinee, [arm, _], MatchSource::Normal) = guard.kind
27+
&& matching_root_macro_call(cx, guard.span, sym::matches_macro).is_some()
3728
&& let Some(binding) = get_pat_binding(cx, scrutinee, outer_arm)
29+
&& !pat_contains_disallowed_or(arm.pat, msrv)
3830
{
3931
let pat_span = match (arm.pat.kind, binding.byref_ident) {
4032
(PatKind::Ref(pat, _), Some(_)) => pat.span,
@@ -53,6 +45,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
5345
// `Some(x) if let Some(2) = x`
5446
else if let ExprKind::Let(let_expr) = guard.kind
5547
&& let Some(binding) = get_pat_binding(cx, let_expr.init, outer_arm)
48+
&& !pat_contains_disallowed_or(let_expr.pat, msrv)
5649
{
5750
let pat_span = match (let_expr.pat.kind, binding.byref_ident) {
5851
(PatKind::Ref(pat, _), Some(_)) => pat.span,

tests/ui/collapsible_match.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
clippy::needless_return,
55
clippy::no_effect,
66
clippy::single_match,
7-
clippy::uninlined_format_args
7+
clippy::uninlined_format_args,
8+
clippy::let_unit_value
89
)]
910

1011
fn lint_cases(opt_opt: Option<Option<u32>>, res_opt: Result<Option<u32>, String>) {
@@ -238,13 +239,22 @@ fn negative_cases(res_opt: Result<Option<u32>, String>, res_res: Result<Result<u
238239
},
239240
_ => return,
240241
}
241-
match make::<Option<E<u32>>>() {
242+
#[clippy::msrv = "1.52.0"]
243+
let _ = match make::<Option<E<u32>>>() {
242244
Some(val) => match val {
243245
E::A(val) | E::B(val) => foo(val),
244246
_ => return,
245247
},
246248
_ => return,
247-
}
249+
};
250+
#[clippy::msrv = "1.53.0"]
251+
let _ = match make::<Option<E<u32>>>() {
252+
Some(val) => match val {
253+
E::A(val) | E::B(val) => foo(val),
254+
_ => return,
255+
},
256+
_ => return,
257+
};
248258
if let Ok(val) = res_opt {
249259
if let Some(n) = val {
250260
let _ = || {

0 commit comments

Comments
 (0)