Skip to content

Commit 7b83f42

Browse files
committed
fix(match_like_matches_macro): don't create matches! with if-let guards
1 parent ce42656 commit 7b83f42

File tree

5 files changed

+93
-2
lines changed

5 files changed

+93
-2
lines changed

clippy_lints/src/matches/match_like_matches.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use super::REDUNDANT_PATTERN_MATCHING;
44
use clippy_utils::diagnostics::span_lint_and_then;
5+
use clippy_utils::higher::has_let_expr;
56
use clippy_utils::source::snippet_with_applicability;
67
use clippy_utils::{is_lint_allowed, is_wild, span_contains_comment};
78
use rustc_ast::LitKind;
@@ -92,7 +93,10 @@ pub(super) fn check_match<'tcx>(
9293
// ```rs
9394
// matches!(e, Either::Left $(if $guard)|+)
9495
// ```
95-
middle_arms.is_empty()
96+
//
97+
// But if the guard _is_ present, it may not be an `if-let` guard, as `matches!` doesn't
98+
// support these (currently?)
99+
(middle_arms.is_empty() && first_arm.guard.is_none_or(|g| !has_let_expr(g)))
96100

97101
// - (added in #6216) There are middle arms
98102
//

tests/ui/match_like_matches_macro.fixed

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,3 +223,10 @@ fn msrv_1_42() {
223223
let _y = matches!(Some(5), Some(0));
224224
//~^^^^ match_like_matches_macro
225225
}
226+
227+
#[expect(clippy::option_option)]
228+
fn issue15841(opt: Option<Option<Option<i32>>>, value: i32) {
229+
// Lint: no if-let _in the guard_
230+
let _ = matches!(opt, Some(first) if (if let Some(second) = first { true } else { todo!() }));
231+
//~^^^^ match_like_matches_macro
232+
}

tests/ui/match_like_matches_macro.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,3 +267,13 @@ fn msrv_1_42() {
267267
};
268268
//~^^^^ match_like_matches_macro
269269
}
270+
271+
#[expect(clippy::option_option)]
272+
fn issue15841(opt: Option<Option<Option<i32>>>, value: i32) {
273+
// Lint: no if-let _in the guard_
274+
let _ = match opt {
275+
Some(first) if (if let Some(second) = first { true } else { todo!() }) => true,
276+
_ => false,
277+
};
278+
//~^^^^ match_like_matches_macro
279+
}

tests/ui/match_like_matches_macro.stderr

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,5 +253,24 @@ LL - };
253253
LL + let _y = matches!(Some(5), Some(0));
254254
|
255255

256-
error: aborting due to 14 previous errors
256+
error: match expression looks like `matches!` macro
257+
--> tests/ui/match_like_matches_macro.rs:274:13
258+
|
259+
LL | let _ = match opt {
260+
| _____________^
261+
LL | | Some(first) if (if let Some(second) = first { true } else { todo!() }) => true,
262+
LL | | _ => false,
263+
LL | | };
264+
| |_____^
265+
|
266+
help: use `matches!` directly
267+
|
268+
LL - let _ = match opt {
269+
LL - Some(first) if (if let Some(second) = first { true } else { todo!() }) => true,
270+
LL - _ => false,
271+
LL - };
272+
LL + let _ = matches!(opt, Some(first) if (if let Some(second) = first { true } else { todo!() }));
273+
|
274+
275+
error: aborting due to 15 previous errors
257276

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//@check-pass
2+
#![warn(clippy::match_like_matches_macro)]
3+
#![feature(if_let_guard)]
4+
5+
#[expect(clippy::option_option)]
6+
fn issue15841(opt: Option<Option<Option<i32>>>, value: i32) {
7+
let _ = match opt {
8+
Some(first)
9+
if let Some(second) = first
10+
&& let Some(third) = second
11+
&& third == value =>
12+
{
13+
true
14+
},
15+
_ => false,
16+
};
17+
18+
// if-let is the second if
19+
let _ = match opt {
20+
Some(first)
21+
if first.is_some()
22+
&& let Some(second) = first =>
23+
{
24+
true
25+
},
26+
_ => false,
27+
};
28+
29+
// if-let is the third if
30+
let _ = match opt {
31+
Some(first)
32+
if first.is_some()
33+
&& first.is_none()
34+
&& let Some(second) = first =>
35+
{
36+
true
37+
},
38+
_ => false,
39+
};
40+
41+
// don't get confused by `or`s
42+
let _ = match opt {
43+
Some(first)
44+
if (first.is_some() || first.is_none())
45+
&& let Some(second) = first =>
46+
{
47+
true
48+
},
49+
_ => false,
50+
};
51+
}

0 commit comments

Comments
 (0)