@@ -6,7 +6,7 @@ use clippy_utils::{
6
6
} ;
7
7
use core:: cmp:: max;
8
8
use rustc_errors:: Applicability ;
9
- use rustc_hir:: { Arm , BindingMode , Block , Expr , ExprKind , Pat , PatKind } ;
9
+ use rustc_hir:: { Arm , BindingMode , Expr , ExprKind , Pat , PatKind } ;
10
10
use rustc_lint:: LateContext ;
11
11
use rustc_middle:: ty:: { self , Ty } ;
12
12
use rustc_span:: { sym, Span } ;
@@ -30,59 +30,46 @@ fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool {
30
30
31
31
#[ rustfmt:: skip]
32
32
pub ( crate ) fn check ( cx : & LateContext < ' _ > , ex : & Expr < ' _ > , arms : & [ Arm < ' _ > ] , expr : & Expr < ' _ > ) {
33
- if arms. len ( ) == 2 && arms[ 0 ] . guard . is_none ( ) && arms[ 1 ] . guard . is_none ( ) {
34
- if expr. span . from_expansion ( ) {
35
- // Don't lint match expressions present in
36
- // macro_rules! block
37
- return ;
38
- }
39
- if let PatKind :: Or ( ..) = arms[ 0 ] . pat . kind {
40
- // don't lint for or patterns for now, this makes
41
- // the lint noisy in unnecessary situations
42
- return ;
43
- }
44
- let els = arms[ 1 ] . body ;
45
- let els = if is_unit_expr ( peel_blocks ( els) ) && !empty_arm_has_comment ( cx, els. span ) {
33
+ if let [ arm1, arm2] = arms
34
+ && arm1. guard . is_none ( )
35
+ && arm2. guard . is_none ( )
36
+ && !expr. span . from_expansion ( )
37
+ // don't lint for or patterns for now, this makes
38
+ // the lint noisy in unnecessary situations
39
+ && !matches ! ( arm1. pat. kind, PatKind :: Or ( ..) )
40
+ {
41
+ let els = if is_unit_expr ( peel_blocks ( arm2. body ) ) && !empty_arm_has_comment ( cx, arm2. body . span ) {
46
42
None
47
- } else if let ExprKind :: Block ( Block { stmts , expr : block_expr , .. } , _) = els . kind {
48
- if stmts. len ( ) == 1 && block_expr . is_none ( ) || stmts . is_empty ( ) && block_expr . is_some ( ) {
43
+ } else if let ExprKind :: Block ( block , _) = arm2 . body . kind {
44
+ if matches ! ( ( block . stmts, block . expr ) , ( [ ] , Some ( _ ) ) | ( [ _ ] , None ) ) {
49
45
// single statement/expr "else" block, don't lint
50
46
return ;
51
47
}
52
48
// block with 2+ statements or 1 expr and 1+ statement
53
- Some ( els )
49
+ Some ( arm2 . body )
54
50
} else {
55
51
// not a block or an empty block w/ comments, don't lint
56
52
return ;
57
53
} ;
58
54
59
55
let ty = cx. typeck_results ( ) . expr_ty ( ex) ;
60
- if ( * ty. kind ( ) != ty:: Bool || is_lint_allowed ( cx, MATCH_BOOL , ex. hir_id ) ) &&
61
- ( check_single_pattern ( arms) || check_opt_like ( cx, arms, ty) ) {
62
- report_single_pattern ( cx, ex, arms, expr, els) ;
56
+ if ( * ty. kind ( ) != ty:: Bool || is_lint_allowed ( cx, MATCH_BOOL , ex. hir_id ) )
57
+ && ( is_wild ( arm2. pat ) || form_exhaustive_matches ( cx, ty, arm1. pat , arm2. pat ) )
58
+ {
59
+ report_single_pattern ( cx, ex, arm1, expr, els) ;
63
60
}
64
61
}
65
62
}
66
63
67
- fn check_single_pattern ( arms : & [ Arm < ' _ > ] ) -> bool {
68
- is_wild ( arms[ 1 ] . pat )
69
- }
70
-
71
- fn report_single_pattern (
72
- cx : & LateContext < ' _ > ,
73
- ex : & Expr < ' _ > ,
74
- arms : & [ Arm < ' _ > ] ,
75
- expr : & Expr < ' _ > ,
76
- els : Option < & Expr < ' _ > > ,
77
- ) {
64
+ fn report_single_pattern ( cx : & LateContext < ' _ > , ex : & Expr < ' _ > , arm : & Arm < ' _ > , expr : & Expr < ' _ > , els : Option < & Expr < ' _ > > ) {
78
65
let lint = if els. is_some ( ) { SINGLE_MATCH_ELSE } else { SINGLE_MATCH } ;
79
66
let ctxt = expr. span . ctxt ( ) ;
80
67
let mut app = Applicability :: MachineApplicable ;
81
68
let els_str = els. map_or ( String :: new ( ) , |els| {
82
69
format ! ( " else {}" , expr_block( cx, els, ctxt, ".." , Some ( expr. span) , & mut app) )
83
70
} ) ;
84
71
85
- let ( pat, pat_ref_count) = peel_hir_pat_refs ( arms [ 0 ] . pat ) ;
72
+ let ( pat, pat_ref_count) = peel_hir_pat_refs ( arm . pat ) ;
86
73
let ( msg, sugg) = if let PatKind :: Path ( _) | PatKind :: Lit ( _) = pat. kind
87
74
&& let ( ty, ty_ref_count) = peel_middle_ty_refs ( cx. typeck_results ( ) . expr_ty ( ex) )
88
75
&& let Some ( spe_trait_id) = cx. tcx . lang_items ( ) . structural_peq_trait ( )
@@ -116,30 +103,24 @@ fn report_single_pattern(
116
103
snippet( cx, ex. span, ".." ) ,
117
104
// PartialEq for different reference counts may not exist.
118
105
"&" . repeat( ref_count_diff) ,
119
- snippet( cx, arms [ 0 ] . pat. span, ".." ) ,
120
- expr_block( cx, arms [ 0 ] . body, ctxt, ".." , Some ( expr. span) , & mut app) ,
106
+ snippet( cx, arm . pat. span, ".." ) ,
107
+ expr_block( cx, arm . body, ctxt, ".." , Some ( expr. span) , & mut app) ,
121
108
) ;
122
109
( msg, sugg)
123
110
} else {
124
111
let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" ;
125
112
let sugg = format ! (
126
113
"if let {} = {} {}{els_str}" ,
127
- snippet( cx, arms [ 0 ] . pat. span, ".." ) ,
114
+ snippet( cx, arm . pat. span, ".." ) ,
128
115
snippet( cx, ex. span, ".." ) ,
129
- expr_block( cx, arms [ 0 ] . body, ctxt, ".." , Some ( expr. span) , & mut app) ,
116
+ expr_block( cx, arm . body, ctxt, ".." , Some ( expr. span) , & mut app) ,
130
117
) ;
131
118
( msg, sugg)
132
119
} ;
133
120
134
121
span_lint_and_sugg ( cx, lint, expr. span , msg, "try" , sugg, app) ;
135
122
}
136
123
137
- fn check_opt_like < ' a > ( cx : & LateContext < ' a > , arms : & [ Arm < ' _ > ] , ty : Ty < ' a > ) -> bool {
138
- // We don't want to lint if the second arm contains an enum which could
139
- // have more variants in the future.
140
- form_exhaustive_matches ( cx, ty, arms[ 0 ] . pat , arms[ 1 ] . pat )
141
- }
142
-
143
124
/// Returns `true` if all of the types in the pattern are enums which we know
144
125
/// won't be expanded in the future
145
126
fn pat_in_candidate_enum < ' a > ( cx : & LateContext < ' a > , ty : Ty < ' a > , pat : & Pat < ' _ > ) -> bool {
0 commit comments