1- #![ allow( unused_variables) ]
21use super :: NOP_MATCH ;
32use clippy_utils:: diagnostics:: span_lint_and_sugg;
43use clippy_utils:: source:: snippet_with_applicability;
5- use clippy_utils:: { eq_expr_value, get_parent_expr} ;
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} ;
66use rustc_errors:: Applicability ;
7- use rustc_hir:: { Arm , BindingAnnotation , Expr , ExprKind , Pat , PatKind , PathSegment , QPath } ;
7+ use rustc_hir:: LangItem :: OptionNone ;
8+ use rustc_hir:: { Arm , BindingAnnotation , Expr , ExprKind , Pat , PatKind , Path , PathSegment , QPath } ;
89use rustc_lint:: LateContext ;
9-
10- pub ( crate ) fn check ( cx : & LateContext < ' _ > , ex : & Expr < ' _ > ) {
11- if false {
12- span_lint_and_sugg (
13- cx,
14- NOP_MATCH ,
15- ex. span ,
16- "this if-let expression is unnecessary" ,
17- "replace it with" ,
18- "" . to_string ( ) ,
19- Applicability :: MachineApplicable ,
20- ) ;
21- }
22- }
10+ use rustc_span:: sym;
2311
2412pub ( crate ) fn check_match ( cx : & LateContext < ' _ > , ex : & Expr < ' _ > , arms : & [ Arm < ' _ > ] ) {
2513 // This is for avoiding collision with `match_single_binding`.
@@ -52,6 +40,70 @@ pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
5240 }
5341}
5442
43+ /// Check for nop `if let` expression that assembled as unnecessary match
44+ ///
45+ /// ```rust,ignore
46+ /// if let Some(a) = option {
47+ /// Some(a)
48+ /// } else {
49+ /// None
50+ /// }
51+ /// ```
52+ /// OR
53+ /// ```rust,ignore
54+ /// if let SomeEnum::A = some_enum {
55+ /// SomeEnum::A
56+ /// } else if let SomeEnum::B = some_enum {
57+ /// SomeEnum::B
58+ /// } else {
59+ /// some_enum
60+ /// }
61+ /// ```
62+ 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 {
68+ let mut applicability = Applicability :: MachineApplicable ;
69+ span_lint_and_sugg(
70+ cx,
71+ NOP_MATCH ,
72+ ex. span,
73+ "this if-let expression is unnecessary" ,
74+ "replace it with" ,
75+ snippet_with_applicability( cx, if_let. let_expr. span, ".." , & mut applicability) . to_string( ) ,
76+ applicability,
77+ ) ;
78+ }
79+ }
80+ }
81+
82+ fn check_if_let ( cx : & LateContext < ' _ > , if_let : & higher:: IfLet < ' _ > ) -> bool {
83+ if let Some ( else_block) = if_let. if_else {
84+ if !pat_same_as_expr ( if_let. let_pat , peel_blocks_with_stmt ( if_let. if_then ) ) {
85+ return false ;
86+ }
87+
88+ let else_expr = peel_blocks_with_stmt ( else_block) ;
89+ // Recurrsively check for each `else if let` phrase,
90+ if let Some ( ref nested_if_let) = higher:: IfLet :: hir ( cx, else_expr) {
91+ return check_if_let ( cx, nested_if_let) ;
92+ }
93+ let ret = strip_return ( else_expr) ;
94+ let let_expr_ty = cx. typeck_results ( ) . expr_ty ( if_let. let_expr ) ;
95+ if is_type_diagnostic_item ( cx, let_expr_ty, sym:: Option ) {
96+ if let ExprKind :: Path ( ref qpath) = ret. kind {
97+ return is_lang_ctor ( cx, qpath, OptionNone ) || eq_expr_value ( cx, if_let. let_expr , ret) ;
98+ }
99+ } else {
100+ return eq_expr_value ( cx, if_let. let_expr , ret) ;
101+ }
102+ return true ;
103+ }
104+ false
105+ }
106+
55107fn strip_return < ' hir > ( expr : & ' hir Expr < ' hir > ) -> & ' hir Expr < ' hir > {
56108 if let ExprKind :: Ret ( Some ( ret) ) = expr. kind {
57109 ret
@@ -68,15 +120,15 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
68120 ExprKind :: Call ( call_expr, [ first_param, ..] ) ,
69121 ) => {
70122 if let ExprKind :: Path ( QPath :: Resolved ( _, call_path) ) = call_expr. kind {
71- if is_identical_segments ( path. segments , call_path. segments )
123+ if has_identical_segments ( path. segments , call_path. segments )
72124 && has_same_non_ref_symbol ( first_pat, first_param)
73125 {
74126 return true ;
75127 }
76128 }
77129 } ,
78130 ( PatKind :: Path ( QPath :: Resolved ( _, p_path) ) , ExprKind :: Path ( QPath :: Resolved ( _, e_path) ) ) => {
79- return is_identical_segments ( p_path. segments , e_path. segments ) ;
131+ return has_identical_segments ( p_path. segments , e_path. segments ) ;
80132 } ,
81133 ( PatKind :: Lit ( pat_lit_expr) , ExprKind :: Lit ( expr_spanned) ) => {
82134 if let ExprKind :: Lit ( pat_spanned) = & pat_lit_expr. kind {
@@ -89,7 +141,7 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
89141 false
90142}
91143
92- fn is_identical_segments ( left_segs : & [ PathSegment < ' _ > ] , right_segs : & [ PathSegment < ' _ > ] ) -> bool {
144+ fn has_identical_segments ( left_segs : & [ PathSegment < ' _ > ] , right_segs : & [ PathSegment < ' _ > ] ) -> bool {
93145 if left_segs. len ( ) != right_segs. len ( ) {
94146 return false ;
95147 }
@@ -105,8 +157,7 @@ fn has_same_non_ref_symbol(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
105157 if_chain ! {
106158 if let PatKind :: Binding ( annot, _, pat_ident, _) = pat. kind;
107159 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( ) ;
160+ if let ExprKind :: Path ( QPath :: Resolved ( _, Path { segments: [ first_seg, ..] , .. } ) ) = expr. kind;
110161 then {
111162 return pat_ident. name == first_seg. ident. name;
112163 }
0 commit comments