11use super :: NEEDLESS_MATCH ;
22use clippy_utils:: diagnostics:: span_lint_and_sugg;
33use 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+ } ;
69use rustc_errors:: Applicability ;
710use 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 } ;
912use rustc_lint:: LateContext ;
1013use rustc_span:: sym;
14+ use rustc_typeck:: hir_ty_to_ty;
1115
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) {
3018 let mut applicability = Applicability :: MachineApplicable ;
3119 span_lint_and_sugg (
3220 cx,
3321 NEEDLESS_MATCH ,
34- match_expr . span ,
22+ expr . span ,
3523 "this match expression is unnecessary" ,
3624 "replace it with" ,
3725 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<'_>])
6048/// }
6149/// ```
6250pub ( 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) {
6853 let mut applicability = Applicability :: MachineApplicable ;
6954 span_lint_and_sugg (
7055 cx,
@@ -79,6 +64,19 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) {
7964 }
8065}
8166
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+
8280fn check_if_let ( cx : & LateContext < ' _ > , if_let : & higher:: IfLet < ' _ > ) -> bool {
8381 if let Some ( if_else) = if_let. if_else {
8482 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 {
10199 if let ExprKind :: Path ( ref qpath) = ret. kind {
102100 return is_lang_ctor ( cx, qpath, OptionNone ) || eq_expr_value ( cx, if_let. let_expr , ret) ;
103101 }
104- } else {
105- return eq_expr_value ( cx, if_let. let_expr , ret) ;
102+ return true ;
106103 }
107- return true ;
104+ return eq_expr_value ( cx , if_let . let_expr , ret ) ;
108105 }
109106 }
107+
110108 false
111109}
112110
@@ -119,14 +117,52 @@ fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
119117 }
120118}
121119
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+
122158fn pat_same_as_expr ( pat : & Pat < ' _ > , expr : & Expr < ' _ > ) -> bool {
123159 let expr = strip_return ( expr) ;
124160 match ( & pat. kind , & expr. kind ) {
125161 // Example: `Some(val) => Some(val)`
126162 ( PatKind :: TupleStruct ( QPath :: Resolved ( _, path) , tuple_params, _) , ExprKind :: Call ( call_expr, call_params) ) => {
127163 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) ;
130166 }
131167 } ,
132168 // Example: `val => val`
@@ -145,7 +181,7 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
145181 } ,
146182 // Example: `Custom::TypeA => Custom::TypeB`, or `None => None`
147183 ( 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 ) ;
149185 } ,
150186 // Example: `5 => 5`
151187 ( PatKind :: Lit ( pat_lit_expr) , ExprKind :: Lit ( expr_spanned) ) => {
@@ -159,19 +195,21 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
159195 false
160196}
161197
162- fn has_identical_segments ( left_segs : & [ PathSegment < ' _ > ] , right_segs : & [ PathSegment < ' _ > ] ) -> bool {
198+ fn same_segments ( left_segs : & [ PathSegment < ' _ > ] , right_segs : & [ PathSegment < ' _ > ] ) -> bool {
163199 if left_segs. len ( ) != right_segs. len ( ) {
164200 return false ;
165201 }
202+
166203 for i in 0 ..left_segs. len ( ) {
167204 if left_segs[ i] . ident . name != right_segs[ i] . ident . name {
168205 return false ;
169206 }
170207 }
208+
171209 true
172210}
173211
174- fn has_same_non_ref_symbols ( pats : & [ Pat < ' _ > ] , exprs : & [ Expr < ' _ > ] ) -> bool {
212+ fn same_non_ref_symbols ( pats : & [ Pat < ' _ > ] , exprs : & [ Expr < ' _ > ] ) -> bool {
175213 if pats. len ( ) != exprs. len ( ) {
176214 return false ;
177215 }
0 commit comments