1
- #![ allow( unused_variables) ]
2
1
use super :: NOP_MATCH ;
3
2
use clippy_utils:: diagnostics:: span_lint_and_sugg;
4
3
use 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} ;
6
6
use 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 } ;
8
9
use 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;
23
11
24
12
pub ( crate ) fn check_match ( cx : & LateContext < ' _ > , ex : & Expr < ' _ > , arms : & [ Arm < ' _ > ] ) {
25
13
// This is for avoiding collision with `match_single_binding`.
@@ -52,6 +40,70 @@ pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
52
40
}
53
41
}
54
42
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
+
55
107
fn strip_return < ' hir > ( expr : & ' hir Expr < ' hir > ) -> & ' hir Expr < ' hir > {
56
108
if let ExprKind :: Ret ( Some ( ret) ) = expr. kind {
57
109
ret
@@ -68,15 +120,15 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
68
120
ExprKind :: Call ( call_expr, [ first_param, ..] ) ,
69
121
) => {
70
122
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 )
72
124
&& has_same_non_ref_symbol ( first_pat, first_param)
73
125
{
74
126
return true ;
75
127
}
76
128
}
77
129
} ,
78
130
( 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 ) ;
80
132
} ,
81
133
( PatKind :: Lit ( pat_lit_expr) , ExprKind :: Lit ( expr_spanned) ) => {
82
134
if let ExprKind :: Lit ( pat_spanned) = & pat_lit_expr. kind {
@@ -89,7 +141,7 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
89
141
false
90
142
}
91
143
92
- fn is_identical_segments ( left_segs : & [ PathSegment < ' _ > ] , right_segs : & [ PathSegment < ' _ > ] ) -> bool {
144
+ fn has_identical_segments ( left_segs : & [ PathSegment < ' _ > ] , right_segs : & [ PathSegment < ' _ > ] ) -> bool {
93
145
if left_segs. len ( ) != right_segs. len ( ) {
94
146
return false ;
95
147
}
@@ -105,8 +157,7 @@ fn has_same_non_ref_symbol(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
105
157
if_chain ! {
106
158
if let PatKind :: Binding ( annot, _, pat_ident, _) = pat. kind;
107
159
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;
110
161
then {
111
162
return pat_ident. name == first_seg. ident. name;
112
163
}
0 commit comments