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