1
1
use super :: NEEDLESS_MATCH ;
2
2
use clippy_utils:: diagnostics:: span_lint_and_sugg;
3
3
use clippy_utils:: source:: snippet_with_applicability;
4
- use clippy_utils:: ty:: is_type_diagnostic_item;
5
- use clippy_utils:: { eq_expr_value, get_parent_expr, higher, is_else_clause, is_lang_ctor, peel_blocks_with_stmt} ;
4
+ use clippy_utils:: ty:: { is_type_diagnostic_item, same_type_and_consts} ;
5
+ use clippy_utils:: {
6
+ eq_expr_value, get_parent_expr_for_hir, get_parent_node, higher, is_else_clause, is_lang_ctor,
7
+ peel_blocks_with_stmt,
8
+ } ;
6
9
use rustc_errors:: Applicability ;
7
10
use rustc_hir:: LangItem :: OptionNone ;
8
- use rustc_hir:: { Arm , BindingAnnotation , Expr , ExprKind , Pat , PatKind , Path , PathSegment , QPath } ;
11
+ use rustc_hir:: { Arm , BindingAnnotation , Expr , ExprKind , FnRetTy , Node , Pat , PatKind , Path , PathSegment , QPath } ;
9
12
use rustc_lint:: LateContext ;
10
13
use rustc_span:: sym;
14
+ use rustc_typeck:: hir_ty_to_ty;
11
15
12
- pub ( crate ) fn check_match ( cx : & LateContext < ' _ > , ex : & Expr < ' _ > , arms : & [ Arm < ' _ > ] ) {
13
- // This is for avoiding collision with `match_single_binding`.
14
- if arms. len ( ) < 2 {
15
- return ;
16
- }
17
-
18
- for arm in arms {
19
- if let PatKind :: Wild = arm. pat . kind {
20
- let ret_expr = strip_return ( arm. body ) ;
21
- if !eq_expr_value ( cx, ex, ret_expr) {
22
- return ;
23
- }
24
- } else if !pat_same_as_expr ( arm. pat , peel_blocks_with_stmt ( arm. body ) ) {
25
- return ;
26
- }
27
- }
28
-
29
- if let Some ( match_expr) = get_parent_expr ( cx, ex) {
16
+ pub ( crate ) fn check_match ( cx : & LateContext < ' _ > , ex : & Expr < ' _ > , arms : & [ Arm < ' _ > ] , expr : & Expr < ' _ > ) {
17
+ if arms. len ( ) > 1 && !is_coercion_casting ( cx, ex, expr) && check_all_arms ( cx, ex, arms) {
30
18
let mut applicability = Applicability :: MachineApplicable ;
31
19
span_lint_and_sugg (
32
20
cx,
33
21
NEEDLESS_MATCH ,
34
- match_expr . span ,
22
+ expr . span ,
35
23
"this match expression is unnecessary" ,
36
24
"replace it with" ,
37
25
snippet_with_applicability ( cx, ex. span , ".." , & mut applicability) . to_string ( ) ,
@@ -60,11 +48,8 @@ pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
60
48
/// }
61
49
/// ```
62
50
pub ( crate ) fn check ( cx : & LateContext < ' _ > , ex : & Expr < ' _ > ) {
63
- if_chain ! {
64
- if let Some ( ref if_let) = higher:: IfLet :: hir( cx, ex) ;
65
- if !is_else_clause( cx. tcx, ex) ;
66
- if check_if_let( cx, if_let) ;
67
- then {
51
+ if let Some ( ref if_let) = higher:: IfLet :: hir ( cx, ex) {
52
+ if !is_else_clause ( cx. tcx , ex) && !is_coercion_casting ( cx, if_let. let_expr , ex) && check_if_let ( cx, if_let) {
68
53
let mut applicability = Applicability :: MachineApplicable ;
69
54
span_lint_and_sugg (
70
55
cx,
@@ -79,6 +64,19 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) {
79
64
}
80
65
}
81
66
67
+ fn check_all_arms ( cx : & LateContext < ' _ > , match_expr : & Expr < ' _ > , arms : & [ Arm < ' _ > ] ) -> bool {
68
+ for arm in arms {
69
+ let arm_expr = peel_blocks_with_stmt ( arm. body ) ;
70
+ if let PatKind :: Wild = arm. pat . kind {
71
+ return eq_expr_value ( cx, match_expr, strip_return ( arm_expr) ) ;
72
+ } else if !pat_same_as_expr ( arm. pat , arm_expr) {
73
+ return false ;
74
+ }
75
+ }
76
+
77
+ true
78
+ }
79
+
82
80
fn check_if_let ( cx : & LateContext < ' _ > , if_let : & higher:: IfLet < ' _ > ) -> bool {
83
81
if let Some ( if_else) = if_let. if_else {
84
82
if !pat_same_as_expr ( if_let. let_pat , peel_blocks_with_stmt ( if_let. if_then ) ) {
@@ -101,12 +99,12 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool {
101
99
if let ExprKind :: Path ( ref qpath) = ret. kind {
102
100
return is_lang_ctor ( cx, qpath, OptionNone ) || eq_expr_value ( cx, if_let. let_expr , ret) ;
103
101
}
104
- } else {
105
- return eq_expr_value ( cx, if_let. let_expr , ret) ;
102
+ return true ;
106
103
}
107
- return true ;
104
+ return eq_expr_value ( cx , if_let . let_expr , ret ) ;
108
105
}
109
106
}
107
+
110
108
false
111
109
}
112
110
@@ -119,14 +117,52 @@ fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
119
117
}
120
118
}
121
119
120
+ /// Manually check for coercion casting by checking if the type of the match operand or let expr
121
+ /// differs with the assigned local variable or the funtion return type.
122
+ fn is_coercion_casting ( cx : & LateContext < ' _ > , match_expr : & Expr < ' _ > , expr : & Expr < ' _ > ) -> bool {
123
+ if let Some ( p_node) = get_parent_node ( cx. tcx , expr. hir_id ) {
124
+ match p_node {
125
+ // Compare match_expr ty with local in `let local = match match_expr {..}`
126
+ Node :: Local ( local) => {
127
+ let results = cx. typeck_results ( ) ;
128
+ return !same_type_and_consts ( results. node_type ( local. hir_id ) , results. expr_ty ( match_expr) ) ;
129
+ } ,
130
+ // compare match_expr ty with RetTy in `fn foo() -> RetTy`
131
+ Node :: Item ( ..) => {
132
+ if let Some ( fn_decl) = p_node. fn_decl ( ) {
133
+ if let FnRetTy :: Return ( ret_ty) = fn_decl. output {
134
+ return !same_type_and_consts (
135
+ hir_ty_to_ty ( cx. tcx , ret_ty) ,
136
+ cx. typeck_results ( ) . expr_ty ( match_expr) ,
137
+ ) ;
138
+ }
139
+ }
140
+ } ,
141
+ // check the parent expr for this whole block `{ match match_expr {..} }`
142
+ Node :: Block ( block) => {
143
+ if let Some ( block_parent_expr) = get_parent_expr_for_hir ( cx, block. hir_id ) {
144
+ return is_coercion_casting ( cx, match_expr, block_parent_expr) ;
145
+ }
146
+ } ,
147
+ // recursively call on `if xxx {..}` etc.
148
+ Node :: Expr ( p_expr) => {
149
+ return is_coercion_casting ( cx, match_expr, p_expr) ;
150
+ } ,
151
+ _ => { } ,
152
+ }
153
+ }
154
+
155
+ false
156
+ }
157
+
122
158
fn pat_same_as_expr ( pat : & Pat < ' _ > , expr : & Expr < ' _ > ) -> bool {
123
159
let expr = strip_return ( expr) ;
124
160
match ( & pat. kind , & expr. kind ) {
125
161
// Example: `Some(val) => Some(val)`
126
162
( PatKind :: TupleStruct ( QPath :: Resolved ( _, path) , tuple_params, _) , ExprKind :: Call ( call_expr, call_params) ) => {
127
163
if let ExprKind :: Path ( QPath :: Resolved ( _, call_path) ) = call_expr. kind {
128
- return has_identical_segments ( path. segments , call_path. segments )
129
- && has_same_non_ref_symbols ( tuple_params, call_params) ;
164
+ return same_segments ( path. segments , call_path. segments )
165
+ && same_non_ref_symbols ( tuple_params, call_params) ;
130
166
}
131
167
} ,
132
168
// Example: `val => val`
@@ -145,7 +181,7 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
145
181
} ,
146
182
// Example: `Custom::TypeA => Custom::TypeB`, or `None => None`
147
183
( PatKind :: Path ( QPath :: Resolved ( _, p_path) ) , ExprKind :: Path ( QPath :: Resolved ( _, e_path) ) ) => {
148
- return has_identical_segments ( p_path. segments , e_path. segments ) ;
184
+ return same_segments ( p_path. segments , e_path. segments ) ;
149
185
} ,
150
186
// Example: `5 => 5`
151
187
( PatKind :: Lit ( pat_lit_expr) , ExprKind :: Lit ( expr_spanned) ) => {
@@ -159,19 +195,21 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
159
195
false
160
196
}
161
197
162
- fn has_identical_segments ( left_segs : & [ PathSegment < ' _ > ] , right_segs : & [ PathSegment < ' _ > ] ) -> bool {
198
+ fn same_segments ( left_segs : & [ PathSegment < ' _ > ] , right_segs : & [ PathSegment < ' _ > ] ) -> bool {
163
199
if left_segs. len ( ) != right_segs. len ( ) {
164
200
return false ;
165
201
}
202
+
166
203
for i in 0 ..left_segs. len ( ) {
167
204
if left_segs[ i] . ident . name != right_segs[ i] . ident . name {
168
205
return false ;
169
206
}
170
207
}
208
+
171
209
true
172
210
}
173
211
174
- fn has_same_non_ref_symbols ( pats : & [ Pat < ' _ > ] , exprs : & [ Expr < ' _ > ] ) -> bool {
212
+ fn same_non_ref_symbols ( pats : & [ Pat < ' _ > ] , exprs : & [ Expr < ' _ > ] ) -> bool {
175
213
if pats. len ( ) != exprs. len ( ) {
176
214
return false ;
177
215
}
0 commit comments