11#![ allow( unused_variables) ]
2+ use super :: NOP_MATCH ;
23use clippy_utils:: diagnostics:: span_lint_and_sugg;
3- use rustc_lint :: LateContext ;
4- use rustc_hir :: { Arm , Expr } ;
4+ use clippy_utils :: source :: snippet_with_applicability ;
5+ use clippy_utils :: { eq_expr_value , get_parent_expr } ;
56use rustc_errors:: Applicability ;
6- use super :: NOP_MATCH ;
7+ use rustc_hir:: { Arm , BindingAnnotation , Expr , ExprKind , Pat , PatKind , PathSegment , QPath } ;
8+ use rustc_lint:: LateContext ;
79
810pub ( crate ) fn check ( cx : & LateContext < ' _ > , ex : & Expr < ' _ > ) {
911 if false {
@@ -20,15 +22,95 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) {
2022}
2123
2224pub ( crate ) fn check_match ( cx : & LateContext < ' _ > , ex : & Expr < ' _ > , arms : & [ Arm < ' _ > ] ) {
23- if false {
25+ // This is for avoiding collision with `match_single_binding`.
26+ if arms. len ( ) < 2 {
27+ return ;
28+ }
29+
30+ for arm in arms {
31+ if let PatKind :: Wild = arm. pat . kind {
32+ let ret_expr = strip_return ( arm. body ) ;
33+ if !eq_expr_value ( cx, ex, ret_expr) {
34+ return ;
35+ }
36+ } else if !pat_same_as_expr ( arm. pat , arm. body ) {
37+ return ;
38+ }
39+ }
40+
41+ if let Some ( match_expr) = get_parent_expr ( cx, ex) {
42+ let mut applicability = Applicability :: MachineApplicable ;
2443 span_lint_and_sugg (
2544 cx,
2645 NOP_MATCH ,
27- ex . span ,
46+ match_expr . span ,
2847 "this match expression is unnecessary" ,
2948 "replace it with" ,
30- "" . to_string ( ) ,
31- Applicability :: MachineApplicable ,
49+ snippet_with_applicability ( cx , ex . span , ".." , & mut applicability ) . to_string ( ) ,
50+ applicability ,
3251 ) ;
3352 }
34- }
53+ }
54+
55+ fn strip_return < ' hir > ( expr : & ' hir Expr < ' hir > ) -> & ' hir Expr < ' hir > {
56+ if let ExprKind :: Ret ( Some ( ret) ) = expr. kind {
57+ ret
58+ } else {
59+ expr
60+ }
61+ }
62+
63+ fn pat_same_as_expr ( pat : & Pat < ' _ > , expr : & Expr < ' _ > ) -> bool {
64+ let expr = strip_return ( expr) ;
65+ match ( & pat. kind , & expr. kind ) {
66+ (
67+ PatKind :: TupleStruct ( QPath :: Resolved ( _, path) , [ first_pat, ..] , _) ,
68+ ExprKind :: Call ( call_expr, [ first_param, ..] ) ,
69+ ) => {
70+ if let ExprKind :: Path ( QPath :: Resolved ( _, call_path) ) = call_expr. kind {
71+ if is_identical_segments ( path. segments , call_path. segments )
72+ && has_same_non_ref_symbol ( first_pat, first_param)
73+ {
74+ return true ;
75+ }
76+ }
77+ } ,
78+ ( PatKind :: Path ( QPath :: Resolved ( _, p_path) ) , ExprKind :: Path ( QPath :: Resolved ( _, e_path) ) ) => {
79+ return is_identical_segments ( p_path. segments , e_path. segments ) ;
80+ } ,
81+ ( PatKind :: Lit ( pat_lit_expr) , ExprKind :: Lit ( expr_spanned) ) => {
82+ if let ExprKind :: Lit ( pat_spanned) = & pat_lit_expr. kind {
83+ return pat_spanned. node == expr_spanned. node ;
84+ }
85+ } ,
86+ _ => { } ,
87+ }
88+
89+ false
90+ }
91+
92+ fn is_identical_segments ( left_segs : & [ PathSegment < ' _ > ] , right_segs : & [ PathSegment < ' _ > ] ) -> bool {
93+ if left_segs. len ( ) != right_segs. len ( ) {
94+ return false ;
95+ }
96+ for i in 0 ..left_segs. len ( ) {
97+ if left_segs[ i] . ident . name != right_segs[ i] . ident . name {
98+ return false ;
99+ }
100+ }
101+ true
102+ }
103+
104+ fn has_same_non_ref_symbol ( pat : & Pat < ' _ > , expr : & Expr < ' _ > ) -> bool {
105+ if_chain ! {
106+ if let PatKind :: Binding ( annot, _, pat_ident, _) = pat. kind;
107+ if !matches!( annot, BindingAnnotation :: Ref | BindingAnnotation :: RefMut ) ;
108+ if let ExprKind :: Path ( QPath :: Resolved ( _, path) ) = expr. kind;
109+ if let Some ( first_seg) = path. segments. first( ) ;
110+ then {
111+ return pat_ident. name == first_seg. ident. name;
112+ }
113+ }
114+
115+ false
116+ }
0 commit comments