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, over,
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 , UnOp } ;
11
+ use rustc_hir:: { Arm , BindingAnnotation , Expr , ExprKind , FnRetTy , Node , Pat , PatKind , Path , 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 , 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 && expr_ty_matches_p_ty ( 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) && expr_ty_matches_p_ty ( 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 ) ) {
@@ -92,18 +90,21 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool {
92
90
93
91
if matches ! ( if_else. kind, ExprKind :: Block ( ..) ) {
94
92
let else_expr = peel_blocks_with_stmt ( if_else) ;
93
+ if matches ! ( else_expr. kind, ExprKind :: Block ( ..) ) {
94
+ return false ;
95
+ }
95
96
let ret = strip_return ( else_expr) ;
96
97
let let_expr_ty = cx. typeck_results ( ) . expr_ty ( if_let. let_expr ) ;
97
98
if is_type_diagnostic_item ( cx, let_expr_ty, sym:: Option ) {
98
99
if let ExprKind :: Path ( ref qpath) = ret. kind {
99
100
return is_lang_ctor ( cx, qpath, OptionNone ) || eq_expr_value ( cx, if_let. let_expr , ret) ;
100
101
}
101
- } else {
102
- return eq_expr_value ( cx, if_let. let_expr , ret) ;
102
+ return true ;
103
103
}
104
- return true ;
104
+ return eq_expr_value ( cx , if_let . let_expr , ret ) ;
105
105
}
106
106
}
107
+
107
108
false
108
109
}
109
110
@@ -116,48 +117,70 @@ fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
116
117
}
117
118
}
118
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 expr_ty_matches_p_ty ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , p_expr : & Expr < ' _ > ) -> bool {
123
+ if let Some ( p_node) = get_parent_node ( cx. tcx , p_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 ( 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 ( hir_ty_to_ty ( cx. tcx , ret_ty) , cx. typeck_results ( ) . expr_ty ( expr) ) ;
135
+ }
136
+ }
137
+ } ,
138
+ // check the parent expr for this whole block `{ match match_expr {..} }`
139
+ Node :: Block ( block) => {
140
+ if let Some ( block_parent_expr) = get_parent_expr_for_hir ( cx, block. hir_id ) {
141
+ return expr_ty_matches_p_ty ( cx, expr, block_parent_expr) ;
142
+ }
143
+ } ,
144
+ // recursively call on `if xxx {..}` etc.
145
+ Node :: Expr ( p_expr) => {
146
+ return expr_ty_matches_p_ty ( cx, expr, p_expr) ;
147
+ } ,
148
+ _ => { } ,
149
+ }
150
+ }
151
+ false
152
+ }
153
+
119
154
fn pat_same_as_expr ( pat : & Pat < ' _ > , expr : & Expr < ' _ > ) -> bool {
120
155
let expr = strip_return ( expr) ;
121
156
match ( & pat. kind , & expr. kind ) {
122
157
// Example: `Some(val) => Some(val)`
123
- (
124
- PatKind :: TupleStruct ( QPath :: Resolved ( _, path) , [ first_pat, ..] , _) ,
125
- ExprKind :: Call ( call_expr, [ first_param, ..] ) ,
126
- ) => {
158
+ ( PatKind :: TupleStruct ( QPath :: Resolved ( _, path) , tuple_params, _) , ExprKind :: Call ( call_expr, call_params) ) => {
127
159
if let ExprKind :: Path ( QPath :: Resolved ( _, call_path) ) = call_expr. kind {
128
- if has_identical_segments ( path. segments , call_path. segments )
129
- && has_same_non_ref_symbol ( first_pat, first_param)
130
- {
131
- return true ;
132
- }
160
+ return over ( path. segments , call_path. segments , |pat_seg, call_seg| {
161
+ pat_seg. ident . name == call_seg. ident . name
162
+ } ) && same_non_ref_symbols ( tuple_params, call_params) ;
133
163
}
134
164
} ,
135
- // Example: `val => val`, or `ref val => *val`
136
- ( PatKind :: Binding ( annot, _, pat_ident, _) , _) => {
137
- let new_expr = if let (
138
- BindingAnnotation :: Ref | BindingAnnotation :: RefMut ,
139
- ExprKind :: Unary ( UnOp :: Deref , operand_expr) ,
140
- ) = ( annot, & expr. kind )
141
- {
142
- operand_expr
143
- } else {
144
- expr
145
- } ;
146
-
147
- if let ExprKind :: Path ( QPath :: Resolved (
165
+ // Example: `val => val`
166
+ (
167
+ PatKind :: Binding ( annot, _, pat_ident, _) ,
168
+ ExprKind :: Path ( QPath :: Resolved (
148
169
_,
149
170
Path {
150
171
segments : [ first_seg, ..] ,
151
172
..
152
173
} ,
153
- ) ) = new_expr . kind
154
- {
155
- return pat_ident . name == first_seg . ident . name ;
156
- }
174
+ ) ) ,
175
+ ) => {
176
+ return ! matches ! ( annot , BindingAnnotation :: Ref | BindingAnnotation :: RefMut )
177
+ && pat_ident . name == first_seg . ident . name ;
157
178
} ,
158
179
// Example: `Custom::TypeA => Custom::TypeB`, or `None => None`
159
180
( PatKind :: Path ( QPath :: Resolved ( _, p_path) ) , ExprKind :: Path ( QPath :: Resolved ( _, e_path) ) ) => {
160
- return has_identical_segments ( p_path. segments , e_path. segments ) ;
181
+ return over ( p_path. segments , e_path. segments , |p_seg, e_seg| {
182
+ p_seg. ident . name == e_seg. ident . name
183
+ } ) ;
161
184
} ,
162
185
// Example: `5 => 5`
163
186
( PatKind :: Lit ( pat_lit_expr) , ExprKind :: Lit ( expr_spanned) ) => {
@@ -171,27 +194,16 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
171
194
false
172
195
}
173
196
174
- fn has_identical_segments ( left_segs : & [ PathSegment < ' _ > ] , right_segs : & [ PathSegment < ' _ > ] ) -> bool {
175
- if left_segs . len ( ) != right_segs . len ( ) {
197
+ fn same_non_ref_symbols ( pats : & [ Pat < ' _ > ] , exprs : & [ Expr < ' _ > ] ) -> bool {
198
+ if pats . len ( ) != exprs . len ( ) {
176
199
return false ;
177
200
}
178
- for i in 0 ..left_segs. len ( ) {
179
- if left_segs[ i] . ident . name != right_segs[ i] . ident . name {
180
- return false ;
181
- }
182
- }
183
- true
184
- }
185
201
186
- fn has_same_non_ref_symbol ( pat : & Pat < ' _ > , expr : & Expr < ' _ > ) -> bool {
187
- if_chain ! {
188
- if let PatKind :: Binding ( annot, _, pat_ident, _) = pat. kind;
189
- if !matches!( annot, BindingAnnotation :: Ref | BindingAnnotation :: RefMut ) ;
190
- if let ExprKind :: Path ( QPath :: Resolved ( _, Path { segments: [ first_seg, ..] , .. } ) ) = expr. kind;
191
- then {
192
- return pat_ident. name == first_seg. ident. name;
202
+ for i in 0 ..pats. len ( ) {
203
+ if !pat_same_as_expr ( & pats[ i] , & exprs[ i] ) {
204
+ return false ;
193
205
}
194
206
}
195
207
196
- false
208
+ true
197
209
}
0 commit comments