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, over,
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 , UnOp } ;
11+ use rustc_hir:: { Arm , BindingAnnotation , Expr , ExprKind , FnRetTy , Node , Pat , PatKind , Path , 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 , 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) {
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) && expr_ty_matches_p_ty ( 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 ) ) {
@@ -92,18 +90,21 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool {
9290
9391 if matches ! ( if_else. kind, ExprKind :: Block ( ..) ) {
9492 let else_expr = peel_blocks_with_stmt ( if_else) ;
93+ if matches ! ( else_expr. kind, ExprKind :: Block ( ..) ) {
94+ return false ;
95+ }
9596 let ret = strip_return ( else_expr) ;
9697 let let_expr_ty = cx. typeck_results ( ) . expr_ty ( if_let. let_expr ) ;
9798 if is_type_diagnostic_item ( cx, let_expr_ty, sym:: Option ) {
9899 if let ExprKind :: Path ( ref qpath) = ret. kind {
99100 return is_lang_ctor ( cx, qpath, OptionNone ) || eq_expr_value ( cx, if_let. let_expr , ret) ;
100101 }
101- } else {
102- return eq_expr_value ( cx, if_let. let_expr , ret) ;
102+ return true ;
103103 }
104- return true ;
104+ return eq_expr_value ( cx , if_let . let_expr , ret ) ;
105105 }
106106 }
107+
107108 false
108109}
109110
@@ -116,48 +117,70 @@ fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
116117 }
117118}
118119
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+
119154fn pat_same_as_expr ( pat : & Pat < ' _ > , expr : & Expr < ' _ > ) -> bool {
120155 let expr = strip_return ( expr) ;
121156 match ( & pat. kind , & expr. kind ) {
122157 // 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) ) => {
127159 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) ;
133163 }
134164 } ,
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 (
148169 _,
149170 Path {
150171 segments : [ first_seg, ..] ,
151172 ..
152173 } ,
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 ;
157178 } ,
158179 // Example: `Custom::TypeA => Custom::TypeB`, or `None => None`
159180 ( 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+ } ) ;
161184 } ,
162185 // Example: `5 => 5`
163186 ( PatKind :: Lit ( pat_lit_expr) , ExprKind :: Lit ( expr_spanned) ) => {
@@ -171,27 +194,16 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
171194 false
172195}
173196
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 ( ) {
176199 return false ;
177200 }
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- }
185201
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 ;
193205 }
194206 }
195207
196- false
208+ true
197209}
0 commit comments