1
+ use clippy_utils:: source:: { snippet_opt, walk_span_to_context} ;
1
2
use clippy_utils:: { meets_msrv, msrvs} ;
2
- use rustc_hir:: { Expr , ExprKind , Local , MatchSource , Pat } ;
3
+ use rustc_hir:: { Arm , Expr , ExprKind , Local , MatchSource , Pat } ;
4
+ use rustc_lexer:: { tokenize, TokenKind } ;
3
5
use rustc_lint:: { LateContext , LateLintPass } ;
4
6
use rustc_semver:: RustcVersion ;
5
7
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
8
+ use rustc_span:: { Span , SpanData , SyntaxContext } ;
6
9
7
10
mod infalliable_detructuring_match;
8
11
mod match_as_ref;
@@ -605,29 +608,33 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
605
608
}
606
609
607
610
if let ExprKind :: Match ( ex, arms, source) = expr. kind {
608
- if source == MatchSource :: Normal {
609
- if !( meets_msrv ( self . msrv . as_ref ( ) , & msrvs:: MATCHES_MACRO )
610
- && match_like_matches:: check_match ( cx, expr, ex, arms) )
611
- {
612
- match_same_arms:: check ( cx, arms) ;
613
- }
611
+ if !contains_cfg_arm ( cx, expr, ex, arms) {
612
+ if source == MatchSource :: Normal {
613
+ if !( meets_msrv ( self . msrv . as_ref ( ) , & msrvs:: MATCHES_MACRO )
614
+ && match_like_matches:: check_match ( cx, expr, ex, arms) )
615
+ {
616
+ match_same_arms:: check ( cx, arms) ;
617
+ }
618
+
619
+ redundant_pattern_match:: check_match ( cx, expr, ex, arms) ;
620
+ single_match:: check ( cx, ex, arms, expr) ;
621
+ match_bool:: check ( cx, ex, arms, expr) ;
622
+ overlapping_arms:: check ( cx, ex, arms) ;
623
+ match_wild_enum:: check ( cx, ex, arms) ;
624
+ match_as_ref:: check ( cx, ex, arms, expr) ;
614
625
615
- redundant_pattern_match:: check_match ( cx, expr, ex, arms) ;
616
- single_match:: check ( cx, ex, arms, expr) ;
617
- match_bool:: check ( cx, ex, arms, expr) ;
618
- overlapping_arms:: check ( cx, ex, arms) ;
619
- match_wild_err_arm:: check ( cx, ex, arms) ;
620
- match_wild_enum:: check ( cx, ex, arms) ;
621
- match_as_ref:: check ( cx, ex, arms, expr) ;
622
- wild_in_or_pats:: check ( cx, arms) ;
623
-
624
- if self . infallible_destructuring_match_linted {
625
- self . infallible_destructuring_match_linted = false ;
626
- } else {
627
- match_single_binding:: check ( cx, ex, arms, expr) ;
626
+ if self . infallible_destructuring_match_linted {
627
+ self . infallible_destructuring_match_linted = false ;
628
+ } else {
629
+ match_single_binding:: check ( cx, ex, arms, expr) ;
630
+ }
628
631
}
632
+ match_ref_pats:: check ( cx, ex, arms. iter ( ) . map ( |el| el. pat ) , expr) ;
629
633
}
630
- match_ref_pats:: check ( cx, ex, arms. iter ( ) . map ( |el| el. pat ) , expr) ;
634
+
635
+ // These don't depend on a relationship between multiple arms
636
+ match_wild_err_arm:: check ( cx, ex, arms) ;
637
+ wild_in_or_pats:: check ( cx, arms) ;
631
638
} else {
632
639
if meets_msrv ( self . msrv . as_ref ( ) , & msrvs:: MATCHES_MACRO ) {
633
640
match_like_matches:: check ( cx, expr) ;
@@ -646,3 +653,73 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
646
653
647
654
extract_msrv_attr ! ( LateContext ) ;
648
655
}
656
+
657
+ /// Checks if there are any arms with a `#[cfg(..)]` attribute.
658
+ fn contains_cfg_arm ( cx : & LateContext < ' _ > , e : & Expr < ' _ > , scrutinee : & Expr < ' _ > , arms : & [ Arm < ' _ > ] ) -> bool {
659
+ let Some ( scrutinee_span) = walk_span_to_context ( scrutinee. span , SyntaxContext :: root ( ) ) else {
660
+ // Shouldn't happen, but treat this as though a `cfg` attribute were found
661
+ return true ;
662
+ } ;
663
+
664
+ let start = scrutinee_span. hi ( ) ;
665
+ let mut arm_spans = arms. iter ( ) . map ( |arm| {
666
+ let data = arm. span . data ( ) ;
667
+ ( data. ctxt == SyntaxContext :: root ( ) ) . then ( || ( data. lo , data. hi ) )
668
+ } ) ;
669
+ let end = e. span . hi ( ) ;
670
+
671
+ let found = arm_spans. try_fold ( start, |start, range| {
672
+ let Some ( ( end, next_start) ) = range else {
673
+ // Shouldn't happen, but treat this as though a `cfg` attribute were found
674
+ return Err ( ( ) ) ;
675
+ } ;
676
+ let span = SpanData {
677
+ lo : start,
678
+ hi : end,
679
+ ctxt : SyntaxContext :: root ( ) ,
680
+ parent : None ,
681
+ }
682
+ . span ( ) ;
683
+ ( !span_contains_cfg ( cx, span) ) . then ( || next_start) . ok_or ( ( ) )
684
+ } ) ;
685
+ match found {
686
+ Ok ( start) => {
687
+ let span = SpanData {
688
+ lo : start,
689
+ hi : end,
690
+ ctxt : SyntaxContext :: root ( ) ,
691
+ parent : None ,
692
+ }
693
+ . span ( ) ;
694
+ span_contains_cfg ( cx, span)
695
+ } ,
696
+ Err ( ( ) ) => true ,
697
+ }
698
+ }
699
+
700
+ fn span_contains_cfg ( cx : & LateContext < ' _ > , s : Span ) -> bool {
701
+ let Some ( snip) = snippet_opt ( cx, s) else {
702
+ // Assume true. This would require either an invalid span, or one which crosses file boundaries.
703
+ return true ;
704
+ } ;
705
+ let mut pos = 0usize ;
706
+ let mut iter = tokenize ( & snip) . map ( |t| {
707
+ let start = pos;
708
+ pos += t. len ;
709
+ ( t. kind , start..pos)
710
+ } ) ;
711
+ while iter. any ( |( t, _) | matches ! ( t, TokenKind :: Pound ) ) {
712
+ let mut iter = iter. by_ref ( ) . skip_while ( |( t, _) | {
713
+ matches ! (
714
+ t,
715
+ TokenKind :: Whitespace | TokenKind :: LineComment { .. } | TokenKind :: BlockComment { .. }
716
+ )
717
+ } ) ;
718
+ if matches ! ( iter. next( ) , Some ( ( TokenKind :: OpenBracket , _) ) )
719
+ && matches ! ( iter. next( ) , Some ( ( TokenKind :: Ident , range) ) if & snip[ range. clone( ) ] == "cfg" )
720
+ {
721
+ return true ;
722
+ }
723
+ }
724
+ false
725
+ }
0 commit comments