@@ -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) {
@@ -202,30 +204,58 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op
202
204
if arms. len ( ) == 2 {
203
205
let node_pair = ( & arms[ 0 ] . pat . kind , & arms[ 1 ] . pat . kind ) ;
204
206
205
- 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) {
206
208
let span = is_expn_of ( expr. span , "matches" ) . unwrap_or ( expr. span . to ( op. span ) ) ;
207
209
let result_expr = match & op. kind {
208
210
ExprKind :: AddrOf ( _, _, borrowed) => borrowed,
209
211
_ => op,
210
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
+
211
241
span_lint_and_sugg (
212
242
cx,
213
243
REDUNDANT_PATTERN_MATCHING ,
214
244
span,
215
245
& format ! ( "redundant pattern matching, consider using `{good_method}`" ) ,
216
246
"try" ,
217
- format ! ( "{}.{good_method}" , snippet ( cx , result_expr . span , "_" ) ) ,
247
+ sugg ,
218
248
Applicability :: MachineApplicable ,
219
249
) ;
220
250
}
221
251
}
222
252
}
223
253
224
- fn found_good_method < ' a > (
254
+ fn found_good_method < ' tcx > (
225
255
cx : & LateContext < ' _ > ,
226
- arms : & [ Arm < ' _ > ] ,
256
+ arms : & ' tcx [ Arm < ' tcx > ] ,
227
257
node : ( & PatKind < ' _ > , & PatKind < ' _ > ) ,
228
- ) -> Option < & ' a str > {
258
+ ) -> Option < ( & ' static str , Option < & ' tcx Guard < ' tcx > > ) > {
229
259
match node {
230
260
(
231
261
PatKind :: TupleStruct ( ref path_left, patterns_left, _) ,
@@ -311,7 +341,11 @@ fn get_ident(path: &QPath<'_>) -> Option<rustc_span::symbol::Ident> {
311
341
}
312
342
}
313
343
314
- 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 > > ) > {
315
349
if let Some ( name) = get_ident ( path_left) {
316
350
return match name. as_str ( ) {
317
351
"Ok" => {
@@ -377,16 +411,16 @@ fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expecte
377
411
}
378
412
379
413
#[ expect( clippy:: too_many_arguments) ]
380
- fn find_good_method_for_match < ' a > (
414
+ fn find_good_method_for_match < ' a , ' tcx > (
381
415
cx : & LateContext < ' _ > ,
382
- arms : & [ Arm < ' _ > ] ,
416
+ arms : & ' tcx [ Arm < ' tcx > ] ,
383
417
path_left : & QPath < ' _ > ,
384
418
path_right : & QPath < ' _ > ,
385
419
expected_item_left : Item ,
386
420
expected_item_right : Item ,
387
421
should_be_left : & ' a str ,
388
422
should_be_right : & ' a str ,
389
- ) -> Option < & ' a str > {
423
+ ) -> Option < ( & ' a str , Option < & ' tcx Guard < ' tcx > > ) > {
390
424
let first_pat = arms[ 0 ] . pat ;
391
425
let second_pat = arms[ 1 ] . pat ;
392
426
@@ -404,22 +438,22 @@ fn find_good_method_for_match<'a>(
404
438
405
439
match body_node_pair {
406
440
( ExprKind :: Lit ( lit_left) , ExprKind :: Lit ( lit_right) ) => match ( & lit_left. node , & lit_right. node ) {
407
- ( LitKind :: Bool ( true ) , LitKind :: Bool ( false ) ) => Some ( should_be_left) ,
408
- ( 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 ( ) ) ) ,
409
443
_ => None ,
410
444
} ,
411
445
_ => None ,
412
446
}
413
447
}
414
448
415
- fn find_good_method_for_matches_macro < ' a > (
449
+ fn find_good_method_for_matches_macro < ' a , ' tcx > (
416
450
cx : & LateContext < ' _ > ,
417
- arms : & [ Arm < ' _ > ] ,
451
+ arms : & ' tcx [ Arm < ' tcx > ] ,
418
452
path_left : & QPath < ' _ > ,
419
453
expected_item_left : Item ,
420
454
should_be_left : & ' a str ,
421
455
should_be_right : & ' a str ,
422
- ) -> Option < & ' a str > {
456
+ ) -> Option < ( & ' a str , Option < & ' tcx Guard < ' tcx > > ) > {
423
457
let first_pat = arms[ 0 ] . pat ;
424
458
425
459
let body_node_pair = if is_pat_variant ( cx, first_pat, path_left, expected_item_left) {
@@ -430,8 +464,8 @@ fn find_good_method_for_matches_macro<'a>(
430
464
431
465
match body_node_pair {
432
466
( ExprKind :: Lit ( lit_left) , ExprKind :: Lit ( lit_right) ) => match ( & lit_left. node , & lit_right. node ) {
433
- ( LitKind :: Bool ( true ) , LitKind :: Bool ( false ) ) => Some ( should_be_left) ,
434
- ( 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 ( ) ) ) ,
435
469
_ => None ,
436
470
} ,
437
471
_ => None ,
0 commit comments