@@ -3,18 +3,20 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
3
3
use clippy_utils:: source:: { snippet, walk_span_to_context} ;
4
4
use clippy_utils:: sugg:: Sugg ;
5
5
use clippy_utils:: ty:: { is_type_diagnostic_item, needs_ordered_drop} ;
6
- use clippy_utils:: visitors:: any_temporaries_need_ordered_drop;
6
+ use clippy_utils:: visitors:: { any_temporaries_need_ordered_drop, for_each_expr } ;
7
7
use clippy_utils:: { higher, is_expn_of, is_trait_method} ;
8
8
use if_chain:: if_chain;
9
9
use rustc_ast:: ast:: LitKind ;
10
10
use rustc_errors:: Applicability ;
11
11
use rustc_hir:: def:: { DefKind , Res } ;
12
12
use rustc_hir:: LangItem :: { self , OptionNone , OptionSome , PollPending , PollReady , ResultErr , ResultOk } ;
13
- use rustc_hir:: { Arm , Expr , ExprKind , Node , Pat , PatKind , QPath , UnOp } ;
13
+ use rustc_hir:: { Arm , Expr , ExprKind , Guard , Node , Pat , PatKind , QPath , UnOp } ;
14
14
use rustc_lint:: LateContext ;
15
15
use rustc_middle:: ty:: subst:: GenericArgKind ;
16
16
use rustc_middle:: ty:: { self , Ty } ;
17
17
use rustc_span:: { sym, Symbol } ;
18
+ use std:: fmt:: Write ;
19
+ use std:: ops:: ControlFlow ;
18
20
19
21
pub ( super ) fn check < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
20
22
if let Some ( higher:: WhileLet { let_pat, let_expr, .. } ) = higher:: WhileLet :: hir ( expr) {
@@ -199,36 +201,61 @@ fn find_sugg_for_if_let<'tcx>(
199
201
}
200
202
201
203
pub ( super ) fn check_match < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > , op : & Expr < ' _ > , arms : & [ Arm < ' _ > ] ) {
202
- if let [ arm1, arm2] = arms
203
- && arm1. guard . is_none ( )
204
- && arm2. guard . is_none ( )
205
- {
206
- let node_pair = ( & arm1. pat . kind , & arm2. pat . kind ) ;
204
+ if arms. len ( ) == 2 {
205
+ let node_pair = ( & arms[ 0 ] . pat . kind , & arms[ 1 ] . pat . kind ) ;
207
206
208
- if let Some ( good_method) = found_good_method ( cx, arms, node_pair) {
207
+ if let Some ( ( good_method, maybe_guard ) ) = found_good_method ( cx, arms, node_pair) {
209
208
let span = is_expn_of ( expr. span , "matches" ) . unwrap_or ( expr. span . to ( op. span ) ) ;
210
209
let result_expr = match & op. kind {
211
210
ExprKind :: AddrOf ( _, _, borrowed) => borrowed,
212
211
_ => op,
213
212
} ;
213
+ let mut sugg = format ! ( "{}.{good_method}" , snippet( cx, result_expr. span, "_" ) ) ;
214
+
215
+ if let Some ( guard) = maybe_guard {
216
+ let Guard :: If ( guard) = * guard else { return } ; // `...is_none() && let ...` is a syntax error
217
+
218
+ // wow, the HIR for match guards in `PAT if let PAT = expr && expr => ...` is annoying!
219
+ // `guard` here is `Guard::If` with the let expression somewhere deep in the tree of exprs,
220
+ // counter to the intuition that it should be `Guard::IfLet`, so we need another check
221
+ // to see that there aren't any let chains anywhere in the guard, as that would break
222
+ // if we suggest `t.is_none() && (let X = y && z)` for:
223
+ // `match t { None if let X = y && z => true, _ => false }`
224
+ let has_nested_let_chain = for_each_expr ( guard, |expr| {
225
+ if matches ! ( expr. kind, ExprKind :: Let ( ..) ) {
226
+ ControlFlow :: Break ( ( ) )
227
+ } else {
228
+ ControlFlow :: Continue ( ( ) )
229
+ }
230
+ } )
231
+ . is_some ( ) ;
232
+
233
+ if has_nested_let_chain {
234
+ return ;
235
+ }
236
+
237
+ let guard = Sugg :: hir ( cx, guard, ".." ) ;
238
+ let _ = write ! ( sugg, " && {}" , guard. maybe_par( ) ) ;
239
+ }
240
+
214
241
span_lint_and_sugg (
215
242
cx,
216
243
REDUNDANT_PATTERN_MATCHING ,
217
244
span,
218
245
& format ! ( "redundant pattern matching, consider using `{good_method}`" ) ,
219
246
"try" ,
220
- format ! ( "{}.{good_method}" , snippet ( cx , result_expr . span , "_" ) ) ,
247
+ sugg ,
221
248
Applicability :: MachineApplicable ,
222
249
) ;
223
250
}
224
251
}
225
252
}
226
253
227
- fn found_good_method < ' a > (
254
+ fn found_good_method < ' tcx > (
228
255
cx : & LateContext < ' _ > ,
229
- arms : & [ Arm < ' _ > ] ,
256
+ arms : & ' tcx [ Arm < ' tcx > ] ,
230
257
node : ( & PatKind < ' _ > , & PatKind < ' _ > ) ,
231
- ) -> Option < & ' a str > {
258
+ ) -> Option < ( & ' static str , Option < & ' tcx Guard < ' tcx > > ) > {
232
259
match node {
233
260
(
234
261
PatKind :: TupleStruct ( ref path_left, patterns_left, _) ,
@@ -314,7 +341,11 @@ fn get_ident(path: &QPath<'_>) -> Option<rustc_span::symbol::Ident> {
314
341
}
315
342
}
316
343
317
- fn get_good_method < ' a > ( cx : & LateContext < ' _ > , arms : & [ Arm < ' _ > ] , path_left : & QPath < ' _ > ) -> Option < & ' a str > {
344
+ fn get_good_method < ' tcx > (
345
+ cx : & LateContext < ' _ > ,
346
+ arms : & ' tcx [ Arm < ' tcx > ] ,
347
+ path_left : & QPath < ' _ > ,
348
+ ) -> Option < ( & ' static str , Option < & ' tcx Guard < ' tcx > > ) > {
318
349
if let Some ( name) = get_ident ( path_left) {
319
350
return match name. as_str ( ) {
320
351
"Ok" => {
@@ -380,16 +411,16 @@ fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expecte
380
411
}
381
412
382
413
#[ expect( clippy:: too_many_arguments) ]
383
- fn find_good_method_for_match < ' a > (
414
+ fn find_good_method_for_match < ' a , ' tcx > (
384
415
cx : & LateContext < ' _ > ,
385
- arms : & [ Arm < ' _ > ] ,
416
+ arms : & ' tcx [ Arm < ' tcx > ] ,
386
417
path_left : & QPath < ' _ > ,
387
418
path_right : & QPath < ' _ > ,
388
419
expected_item_left : Item ,
389
420
expected_item_right : Item ,
390
421
should_be_left : & ' a str ,
391
422
should_be_right : & ' a str ,
392
- ) -> Option < & ' a str > {
423
+ ) -> Option < ( & ' a str , Option < & ' tcx Guard < ' tcx > > ) > {
393
424
let first_pat = arms[ 0 ] . pat ;
394
425
let second_pat = arms[ 1 ] . pat ;
395
426
@@ -407,22 +438,22 @@ fn find_good_method_for_match<'a>(
407
438
408
439
match body_node_pair {
409
440
( ExprKind :: Lit ( lit_left) , ExprKind :: Lit ( lit_right) ) => match ( & lit_left. node , & lit_right. node ) {
410
- ( LitKind :: Bool ( true ) , LitKind :: Bool ( false ) ) => Some ( should_be_left) ,
411
- ( LitKind :: Bool ( false ) , LitKind :: Bool ( true ) ) => Some ( should_be_right) ,
441
+ ( LitKind :: Bool ( true ) , LitKind :: Bool ( false ) ) => Some ( ( should_be_left, arms [ 0 ] . guard . as_ref ( ) ) ) ,
442
+ ( LitKind :: Bool ( false ) , LitKind :: Bool ( true ) ) => Some ( ( should_be_right, arms [ 1 ] . guard . as_ref ( ) ) ) ,
412
443
_ => None ,
413
444
} ,
414
445
_ => None ,
415
446
}
416
447
}
417
448
418
- fn find_good_method_for_matches_macro < ' a > (
449
+ fn find_good_method_for_matches_macro < ' a , ' tcx > (
419
450
cx : & LateContext < ' _ > ,
420
- arms : & [ Arm < ' _ > ] ,
451
+ arms : & ' tcx [ Arm < ' tcx > ] ,
421
452
path_left : & QPath < ' _ > ,
422
453
expected_item_left : Item ,
423
454
should_be_left : & ' a str ,
424
455
should_be_right : & ' a str ,
425
- ) -> Option < & ' a str > {
456
+ ) -> Option < ( & ' a str , Option < & ' tcx Guard < ' tcx > > ) > {
426
457
let first_pat = arms[ 0 ] . pat ;
427
458
428
459
let body_node_pair = if is_pat_variant ( cx, first_pat, path_left, expected_item_left) {
@@ -433,8 +464,8 @@ fn find_good_method_for_matches_macro<'a>(
433
464
434
465
match body_node_pair {
435
466
( ExprKind :: Lit ( lit_left) , ExprKind :: Lit ( lit_right) ) => match ( & lit_left. node , & lit_right. node ) {
436
- ( LitKind :: Bool ( true ) , LitKind :: Bool ( false ) ) => Some ( should_be_left) ,
437
- ( LitKind :: Bool ( false ) , LitKind :: Bool ( true ) ) => Some ( should_be_right) ,
467
+ ( LitKind :: Bool ( true ) , LitKind :: Bool ( false ) ) => Some ( ( should_be_left, arms [ 0 ] . guard . as_ref ( ) ) ) ,
468
+ ( LitKind :: Bool ( false ) , LitKind :: Bool ( true ) ) => Some ( ( should_be_right, arms [ 1 ] . guard . as_ref ( ) ) ) ,
438
469
_ => None ,
439
470
} ,
440
471
_ => None ,
0 commit comments