@@ -3,6 +3,7 @@ use clippy_utils::diagnostics::{
3
3
multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
4
4
} ;
5
5
use clippy_utils:: macros:: { is_panic, root_macro_call} ;
6
+ use clippy_utils:: peel_blocks_with_stmt;
6
7
use clippy_utils:: source:: { expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability} ;
7
8
use clippy_utils:: sugg:: Sugg ;
8
9
use clippy_utils:: ty:: { implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs} ;
@@ -12,28 +13,28 @@ use clippy_utils::{
12
13
path_to_local, path_to_local_id, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns,
13
14
strip_pat_refs,
14
15
} ;
15
- use clippy_utils:: { higher, peel_blocks_with_stmt} ;
16
16
use clippy_utils:: { paths, search_same, SpanlessEq , SpanlessHash } ;
17
- use core:: iter:: { once, ExactSizeIterator } ;
17
+ use core:: iter:: once;
18
18
use if_chain:: if_chain;
19
- use rustc_ast:: ast:: { Attribute , LitKind } ;
19
+ use rustc_ast:: ast:: LitKind ;
20
20
use rustc_errors:: Applicability ;
21
21
use rustc_hir:: def:: { CtorKind , DefKind , Res } ;
22
22
use rustc_hir:: LangItem :: { OptionNone , OptionSome } ;
23
23
use rustc_hir:: {
24
- self as hir, Arm , BindingAnnotation , Block , BorrowKind , Expr , ExprKind , Guard , HirId , Local , MatchSource ,
25
- Mutability , Node , Pat , PatKind , PathSegment , QPath , RangeEnd , TyKind ,
24
+ self as hir, Arm , BindingAnnotation , Block , BorrowKind , Expr , ExprKind , HirId , Local , MatchSource , Mutability ,
25
+ Node , Pat , PatKind , PathSegment , QPath , RangeEnd , TyKind ,
26
26
} ;
27
27
use rustc_hir:: { HirIdMap , HirIdSet } ;
28
28
use rustc_lint:: { LateContext , LateLintPass } ;
29
29
use rustc_middle:: ty:: { self , Ty , TyS , VariantDef } ;
30
30
use rustc_semver:: RustcVersion ;
31
31
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
32
- use rustc_span:: source_map:: { Span , Spanned } ;
33
- use rustc_span:: { sym, symbol:: kw} ;
32
+ use rustc_span:: { sym, symbol:: kw, Span } ;
34
33
use std:: cmp:: { max, Ordering } ;
35
34
use std:: collections:: hash_map:: Entry ;
36
35
36
+ mod match_like_matches;
37
+
37
38
declare_clippy_lint ! {
38
39
/// ### What it does
39
40
/// Checks for matches with a single arm where an `if let`
@@ -622,7 +623,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
622
623
redundant_pattern_match:: check ( cx, expr) ;
623
624
624
625
if meets_msrv ( self . msrv . as_ref ( ) , & msrvs:: MATCHES_MACRO ) {
625
- if !check_match_like_matches ( cx, expr) {
626
+ if !match_like_matches :: check ( cx, expr) {
626
627
lint_match_arms ( cx, expr) ;
627
628
}
628
629
} else {
@@ -1382,161 +1383,6 @@ fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
1382
1383
}
1383
1384
}
1384
1385
1385
- /// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
1386
- fn check_match_like_matches < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) -> bool {
1387
- if let Some ( higher:: IfLet {
1388
- let_pat,
1389
- let_expr,
1390
- if_then,
1391
- if_else : Some ( if_else) ,
1392
- } ) = higher:: IfLet :: hir ( cx, expr)
1393
- {
1394
- return find_matches_sugg (
1395
- cx,
1396
- let_expr,
1397
- IntoIterator :: into_iter ( [ ( & [ ] [ ..] , Some ( let_pat) , if_then, None ) , ( & [ ] [ ..] , None , if_else, None ) ] ) ,
1398
- expr,
1399
- true ,
1400
- ) ;
1401
- }
1402
-
1403
- if let ExprKind :: Match ( scrut, arms, MatchSource :: Normal ) = expr. kind {
1404
- return find_matches_sugg (
1405
- cx,
1406
- scrut,
1407
- arms. iter ( ) . map ( |arm| {
1408
- (
1409
- cx. tcx . hir ( ) . attrs ( arm. hir_id ) ,
1410
- Some ( arm. pat ) ,
1411
- arm. body ,
1412
- arm. guard . as_ref ( ) ,
1413
- )
1414
- } ) ,
1415
- expr,
1416
- false ,
1417
- ) ;
1418
- }
1419
-
1420
- false
1421
- }
1422
-
1423
- /// Lint a `match` or `if let` for replacement by `matches!`
1424
- fn find_matches_sugg < ' a , ' b , I > (
1425
- cx : & LateContext < ' _ > ,
1426
- ex : & Expr < ' _ > ,
1427
- mut iter : I ,
1428
- expr : & Expr < ' _ > ,
1429
- is_if_let : bool ,
1430
- ) -> bool
1431
- where
1432
- ' b : ' a ,
1433
- I : Clone
1434
- + DoubleEndedIterator
1435
- + ExactSizeIterator
1436
- + Iterator <
1437
- Item = (
1438
- & ' a [ Attribute ] ,
1439
- Option < & ' a Pat < ' b > > ,
1440
- & ' a Expr < ' b > ,
1441
- Option < & ' a Guard < ' b > > ,
1442
- ) ,
1443
- > ,
1444
- {
1445
- if_chain ! {
1446
- if iter. len( ) >= 2 ;
1447
- if cx. typeck_results( ) . expr_ty( expr) . is_bool( ) ;
1448
- if let Some ( ( _, last_pat_opt, last_expr, _) ) = iter. next_back( ) ;
1449
- let iter_without_last = iter. clone( ) ;
1450
- if let Some ( ( first_attrs, _, first_expr, first_guard) ) = iter. next( ) ;
1451
- if let Some ( b0) = find_bool_lit( & first_expr. kind, is_if_let) ;
1452
- if let Some ( b1) = find_bool_lit( & last_expr. kind, is_if_let) ;
1453
- if b0 != b1;
1454
- if first_guard. is_none( ) || iter. len( ) == 0 ;
1455
- if first_attrs. is_empty( ) ;
1456
- if iter
1457
- . all( |arm| {
1458
- find_bool_lit( & arm. 2 . kind, is_if_let) . map_or( false , |b| b == b0) && arm. 3 . is_none( ) && arm. 0 . is_empty( )
1459
- } ) ;
1460
- then {
1461
- if let Some ( last_pat) = last_pat_opt {
1462
- if !is_wild( last_pat) {
1463
- return false ;
1464
- }
1465
- }
1466
-
1467
- // The suggestion may be incorrect, because some arms can have `cfg` attributes
1468
- // evaluated into `false` and so such arms will be stripped before.
1469
- let mut applicability = Applicability :: MaybeIncorrect ;
1470
- let pat = {
1471
- use itertools:: Itertools as _;
1472
- iter_without_last
1473
- . filter_map( |arm| {
1474
- let pat_span = arm. 1 ?. span;
1475
- Some ( snippet_with_applicability( cx, pat_span, ".." , & mut applicability) )
1476
- } )
1477
- . join( " | " )
1478
- } ;
1479
- let pat_and_guard = if let Some ( Guard :: If ( g) ) = first_guard {
1480
- format!( "{} if {}" , pat, snippet_with_applicability( cx, g. span, ".." , & mut applicability) )
1481
- } else {
1482
- pat
1483
- } ;
1484
-
1485
- // strip potential borrows (#6503), but only if the type is a reference
1486
- let mut ex_new = ex;
1487
- if let ExprKind :: AddrOf ( BorrowKind :: Ref , .., ex_inner) = ex. kind {
1488
- if let ty:: Ref ( ..) = cx. typeck_results( ) . expr_ty( ex_inner) . kind( ) {
1489
- ex_new = ex_inner;
1490
- }
1491
- } ;
1492
- span_lint_and_sugg(
1493
- cx,
1494
- MATCH_LIKE_MATCHES_MACRO ,
1495
- expr. span,
1496
- & format!( "{} expression looks like `matches!` macro" , if is_if_let { "if let .. else" } else { "match" } ) ,
1497
- "try this" ,
1498
- format!(
1499
- "{}matches!({}, {})" ,
1500
- if b0 { "" } else { "!" } ,
1501
- snippet_with_applicability( cx, ex_new. span, ".." , & mut applicability) ,
1502
- pat_and_guard,
1503
- ) ,
1504
- applicability,
1505
- ) ;
1506
- true
1507
- } else {
1508
- false
1509
- }
1510
- }
1511
- }
1512
-
1513
- /// Extract a `bool` or `{ bool }`
1514
- fn find_bool_lit ( ex : & ExprKind < ' _ > , is_if_let : bool ) -> Option < bool > {
1515
- match ex {
1516
- ExprKind :: Lit ( Spanned {
1517
- node : LitKind :: Bool ( b) , ..
1518
- } ) => Some ( * b) ,
1519
- ExprKind :: Block (
1520
- rustc_hir:: Block {
1521
- stmts : & [ ] ,
1522
- expr : Some ( exp) ,
1523
- ..
1524
- } ,
1525
- _,
1526
- ) if is_if_let => {
1527
- if let ExprKind :: Lit ( Spanned {
1528
- node : LitKind :: Bool ( b) , ..
1529
- } ) = exp. kind
1530
- {
1531
- Some ( b)
1532
- } else {
1533
- None
1534
- }
1535
- } ,
1536
- _ => None ,
1537
- }
1538
- }
1539
-
1540
1386
#[ allow( clippy:: too_many_lines) ]
1541
1387
fn check_match_single_binding < ' a > ( cx : & LateContext < ' a > , ex : & Expr < ' a > , arms : & [ Arm < ' _ > ] , expr : & Expr < ' _ > ) {
1542
1388
if expr. span . from_expansion ( ) || arms. len ( ) != 1 || is_refutable ( cx, arms[ 0 ] . pat ) {
0 commit comments