11use clippy_utils:: source:: { snippet_opt, span_starts_with, walk_span_to_context} ;
2- use clippy_utils:: { meets_msrv, msrvs} ;
2+ use clippy_utils:: { higher , meets_msrv, msrvs} ;
33use rustc_hir:: { Arm , Expr , ExprKind , Local , MatchSource , Pat } ;
44use rustc_lexer:: { tokenize, TokenKind } ;
5- use rustc_lint:: { LateContext , LateLintPass } ;
5+ use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
6+ use rustc_middle:: lint:: in_external_macro;
67use rustc_semver:: RustcVersion ;
78use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
89use rustc_span:: { Span , SpanData , SyntaxContext } ;
910
11+ mod collapsible_match;
1012mod infallible_destructuring_match;
1113mod match_as_ref;
1214mod match_bool;
@@ -610,6 +612,44 @@ declare_clippy_lint! {
610612 "`match` or match-like `if let` that are unnecessary"
611613}
612614
615+ declare_clippy_lint ! {
616+ /// ### What it does
617+ /// Finds nested `match` or `if let` expressions where the patterns may be "collapsed" together
618+ /// without adding any branches.
619+ ///
620+ /// Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only
621+ /// cases where merging would most likely make the code more readable.
622+ ///
623+ /// ### Why is this bad?
624+ /// It is unnecessarily verbose and complex.
625+ ///
626+ /// ### Example
627+ /// ```rust
628+ /// fn func(opt: Option<Result<u64, String>>) {
629+ /// let n = match opt {
630+ /// Some(n) => match n {
631+ /// Ok(n) => n,
632+ /// _ => return,
633+ /// }
634+ /// None => return,
635+ /// };
636+ /// }
637+ /// ```
638+ /// Use instead:
639+ /// ```rust
640+ /// fn func(opt: Option<Result<u64, String>>) {
641+ /// let n = match opt {
642+ /// Some(Ok(n)) => n,
643+ /// _ => return,
644+ /// };
645+ /// }
646+ /// ```
647+ #[ clippy:: version = "1.50.0" ]
648+ pub COLLAPSIBLE_MATCH ,
649+ style,
650+ "Nested `match` or `if let` expressions where the patterns may be \" collapsed\" together."
651+ }
652+
613653#[ derive( Default ) ]
614654pub struct Matches {
615655 msrv : Option < RustcVersion > ,
@@ -644,19 +684,29 @@ impl_lint_pass!(Matches => [
644684 MATCH_LIKE_MATCHES_MACRO ,
645685 MATCH_SAME_ARMS ,
646686 NEEDLESS_MATCH ,
687+ COLLAPSIBLE_MATCH ,
647688] ) ;
648689
649690impl < ' tcx > LateLintPass < ' tcx > for Matches {
650691 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
651- if expr. span . from_expansion ( ) {
692+ if in_external_macro ( cx . sess ( ) , expr. span ) {
652693 return ;
653694 }
695+ let from_expansion = expr. span . from_expansion ( ) ;
654696
655697 if let ExprKind :: Match ( ex, arms, source) = expr. kind {
656698 if !span_starts_with ( cx, expr. span , "match" ) {
657699 return ;
658700 }
659- if !contains_cfg_arm ( cx, expr, ex, arms) {
701+
702+ collapsible_match:: check_match ( cx, arms) ;
703+ if !from_expansion {
704+ // These don't depend on a relationship between multiple arms
705+ match_wild_err_arm:: check ( cx, ex, arms) ;
706+ wild_in_or_pats:: check ( cx, arms) ;
707+ }
708+
709+ if !from_expansion && !contains_cfg_arm ( cx, expr, ex, arms) {
660710 if source == MatchSource :: Normal {
661711 if !( meets_msrv ( self . msrv , msrvs:: MATCHES_MACRO )
662712 && match_like_matches:: check_match ( cx, expr, ex, arms) )
@@ -680,16 +730,32 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
680730 }
681731 match_ref_pats:: check ( cx, ex, arms. iter ( ) . map ( |el| el. pat ) , expr) ;
682732 }
683-
684- // These don't depend on a relationship between multiple arms
685- match_wild_err_arm:: check ( cx, ex, arms) ;
686- wild_in_or_pats:: check ( cx, arms) ;
687- } else {
688- if meets_msrv ( self . msrv , msrvs:: MATCHES_MACRO ) {
689- match_like_matches:: check ( cx, expr) ;
733+ } else if let Some ( if_let) = higher:: IfLet :: hir ( cx, expr) {
734+ collapsible_match:: check_if_let ( cx, if_let. let_pat , if_let. if_then , if_let. if_else ) ;
735+ if !from_expansion {
736+ if let Some ( else_expr) = if_let. if_else {
737+ if meets_msrv ( self . msrv , msrvs:: MATCHES_MACRO ) {
738+ match_like_matches:: check_if_let (
739+ cx,
740+ expr,
741+ if_let. let_pat ,
742+ if_let. let_expr ,
743+ if_let. if_then ,
744+ else_expr,
745+ ) ;
746+ }
747+ }
748+ redundant_pattern_match:: check_if_let (
749+ cx,
750+ expr,
751+ if_let. let_pat ,
752+ if_let. let_expr ,
753+ if_let. if_else . is_some ( ) ,
754+ ) ;
755+ needless_match:: check_if_let ( cx, expr, & if_let) ;
690756 }
757+ } else if !from_expansion {
691758 redundant_pattern_match:: check ( cx, expr) ;
692- needless_match:: check ( cx, expr) ;
693759 }
694760 }
695761
0 commit comments