@@ -3,6 +3,7 @@ use clippy_utils::diagnostics::{
33 multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
44} ;
55use clippy_utils:: macros:: { is_panic, root_macro_call} ;
6+ use clippy_utils:: peel_blocks_with_stmt;
67use clippy_utils:: source:: { expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability} ;
78use clippy_utils:: sugg:: Sugg ;
89use clippy_utils:: ty:: { implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs} ;
@@ -12,28 +13,28 @@ use clippy_utils::{
1213 path_to_local, path_to_local_id, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns,
1314 strip_pat_refs,
1415} ;
15- use clippy_utils:: { higher, peel_blocks_with_stmt} ;
1616use clippy_utils:: { paths, search_same, SpanlessEq , SpanlessHash } ;
17- use core:: iter:: { once, ExactSizeIterator } ;
17+ use core:: iter:: once;
1818use if_chain:: if_chain;
19- use rustc_ast:: ast:: { Attribute , LitKind } ;
19+ use rustc_ast:: ast:: LitKind ;
2020use rustc_errors:: Applicability ;
2121use rustc_hir:: def:: { CtorKind , DefKind , Res } ;
2222use rustc_hir:: LangItem :: { OptionNone , OptionSome } ;
2323use 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 ,
2626} ;
2727use rustc_hir:: { HirIdMap , HirIdSet } ;
2828use rustc_lint:: { LateContext , LateLintPass } ;
2929use rustc_middle:: ty:: { self , Ty , TyS , VariantDef } ;
3030use rustc_semver:: RustcVersion ;
3131use 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 } ;
3433use std:: cmp:: { max, Ordering } ;
3534use std:: collections:: hash_map:: Entry ;
3635
36+ mod match_like_matches;
37+
3738declare_clippy_lint ! {
3839 /// ### What it does
3940 /// Checks for matches with a single arm where an `if let`
@@ -622,7 +623,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
622623 redundant_pattern_match:: check ( cx, expr) ;
623624
624625 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) {
626627 lint_match_arms ( cx, expr) ;
627628 }
628629 } else {
@@ -1382,161 +1383,6 @@ fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
13821383 }
13831384}
13841385
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-
15401386#[ allow( clippy:: too_many_lines) ]
15411387fn check_match_single_binding < ' a > ( cx : & LateContext < ' a > , ex : & Expr < ' a > , arms : & [ Arm < ' _ > ] , expr : & Expr < ' _ > ) {
15421388 if expr. span . from_expansion ( ) || arms. len ( ) != 1 || is_refutable ( cx, arms[ 0 ] . pat ) {
0 commit comments