1
- use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
1
use clippy_utils:: source:: snippet_with_applicability;
3
2
use clippy_utils:: ty:: implements_trait;
3
+ use clippy_utils:: { diagnostics:: span_lint_and_sugg, higher:: MatchesExpn } ;
4
4
use if_chain:: if_chain;
5
5
use rustc_errors:: Applicability ;
6
- use rustc_hir:: { Expr , ExprKind , Pat , PatKind } ;
7
- use rustc_lint:: { LateContext , LateLintPass } ;
8
- use rustc_middle:: ty:: Ty ;
6
+ use rustc_hir:: {
7
+ def:: { DefKind , Res } ,
8
+ Arm , Expr , ExprKind , Pat , PatKind , QPath ,
9
+ } ;
10
+ use rustc_lint:: { LateContext , LateLintPass , Lint } ;
11
+ use rustc_middle:: ty:: { Adt , Ty } ;
9
12
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
13
+ use rustc_span:: Span ;
10
14
11
15
declare_clippy_lint ! {
12
16
/// ### What it does
13
- /// Checks for pattern matchings that can be expressed using equality.
17
+ /// Checks for `if let <pat> = <expr>` (and `while let` and similars) that can be expressed
18
+ /// using `if <expr> == <pat>`.
14
19
///
15
20
/// ### Why is this bad?
16
21
///
@@ -33,68 +38,192 @@ declare_clippy_lint! {
33
38
/// }
34
39
/// ```
35
40
pub EQUATABLE_IF_LET ,
36
- nursery ,
37
- "using pattern matching instead of equality"
41
+ style ,
42
+ "using if let instead of if with a equality condition "
38
43
}
39
44
40
- declare_lint_pass ! ( PatternEquality => [ EQUATABLE_IF_LET ] ) ;
45
+ declare_clippy_lint ! {
46
+ /// ### What it does
47
+ /// Checks for `matches!(<expr>, <pat>)` that can be expressed
48
+ /// using `<expr> == <pat>`.
49
+ ///
50
+ /// ### Why is this bad?
51
+ ///
52
+ /// It is less concise and less clear.
53
+ ///
54
+ /// ### Example
55
+ /// ```rust,ignore
56
+ /// let condition = matches!(x, Some(2));
57
+ /// ```
58
+ /// Should be written
59
+ /// ```rust,ignore
60
+ /// let condition = x == Some(2);
61
+ /// ```
62
+ pub EQUATABLE_MATCHES ,
63
+ pedantic,
64
+ "using `matches!` instead of equality"
65
+ }
66
+
67
+ declare_lint_pass ! ( PatternEquality => [ EQUATABLE_IF_LET , EQUATABLE_MATCHES ] ) ;
41
68
42
- /// detects if pattern matches just one thing
43
- fn unary_pattern ( pat : & Pat < ' _ > ) -> bool {
44
- fn array_rec ( pats : & [ Pat < ' _ > ] ) -> bool {
45
- pats. iter ( ) . all ( unary_pattern)
69
+ fn equatable_pattern ( cx : & LateContext < ' _ > , pat : & Pat < ' _ > ) -> bool {
70
+ fn array_rec ( cx : & LateContext < ' _ > , pats : & [ Pat < ' _ > ] ) -> bool {
71
+ pats. iter ( ) . all ( |x| equatable_pattern ( cx, x) )
46
72
}
47
- match & pat. kind {
48
- PatKind :: Slice ( _, _, _) | PatKind :: Range ( _, _, _) | PatKind :: Binding ( ..) | PatKind :: Wild | PatKind :: Or ( _) => {
73
+ fn is_derived ( cx : & LateContext < ' _ > , pat : & Pat < ' _ > ) -> bool {
74
+ let ty = cx. typeck_results ( ) . pat_ty ( pat) ;
75
+ if let Some ( def_id) = cx. tcx . lang_items ( ) . structural_peq_trait ( ) {
76
+ implements_trait ( cx, ty, def_id, & [ ty. into ( ) ] )
77
+ } else {
49
78
false
79
+ }
80
+ }
81
+ match & pat. kind {
82
+ PatKind :: Slice ( a, None , [ ] ) => array_rec ( cx, a) ,
83
+ PatKind :: Struct ( _, a, etc) => !etc && is_derived ( cx, pat) && a. iter ( ) . all ( |x| equatable_pattern ( cx, x. pat ) ) ,
84
+ PatKind :: Tuple ( a, etc) => !etc. is_some ( ) && array_rec ( cx, a) ,
85
+ PatKind :: TupleStruct ( _, a, etc) => !etc. is_some ( ) && is_derived ( cx, pat) && array_rec ( cx, a) ,
86
+ PatKind :: Ref ( x, _) | PatKind :: Box ( x) => equatable_pattern ( cx, x) ,
87
+ PatKind :: Path ( QPath :: Resolved ( _, b) ) => match b. res {
88
+ Res :: Def ( DefKind :: Const , _) => true ,
89
+ _ => is_derived ( cx, pat) ,
50
90
} ,
51
- PatKind :: Struct ( _, a, etc) => !etc && a. iter ( ) . all ( |x| unary_pattern ( x. pat ) ) ,
52
- PatKind :: Tuple ( a, etc) | PatKind :: TupleStruct ( _, a, etc) => !etc. is_some ( ) && array_rec ( a) ,
53
- PatKind :: Ref ( x, _) | PatKind :: Box ( x) => unary_pattern ( x) ,
54
- PatKind :: Path ( _) | PatKind :: Lit ( _) => true ,
91
+ PatKind :: Path ( _) => is_derived ( cx, pat) ,
92
+ PatKind :: Lit ( _) => true ,
93
+ PatKind :: Slice ( ..) | PatKind :: Range ( ..) | PatKind :: Binding ( ..) | PatKind :: Wild | PatKind :: Or ( _) => false ,
55
94
}
56
95
}
57
96
58
- fn is_structural_partial_eq ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > , other : Ty < ' tcx > ) -> bool {
97
+ fn is_partial_eq ( cx : & LateContext < ' tcx > , t1 : Ty < ' tcx > , t2 : Ty < ' tcx > ) -> bool {
59
98
if let Some ( def_id) = cx. tcx . lang_items ( ) . eq_trait ( ) {
60
- implements_trait ( cx, ty , def_id, & [ other . into ( ) ] )
99
+ implements_trait ( cx, t1 , def_id, & [ t2 . into ( ) ] )
61
100
} else {
62
101
false
63
102
}
64
103
}
65
104
105
+ fn pat_to_string ( cx : & LateContext < ' tcx > , app : & mut Applicability , pat : & Pat < ' _ > , goal : Ty < ' _ > ) -> Option < String > {
106
+ fn inner ( cx : & LateContext < ' tcx > , app : & mut Applicability , pat : & Pat < ' _ > , goal : Ty < ' _ > , r : & mut String ) -> bool {
107
+ let ty = cx. typeck_results ( ) . pat_ty ( pat) ;
108
+ if ty == goal {
109
+ match & pat. kind {
110
+ PatKind :: TupleStruct ( q, ..) | PatKind :: Struct ( q, ..) => {
111
+ let ( adt_def, generic_args) = if let Adt ( x, y) = ty. kind ( ) {
112
+ ( x, y)
113
+ } else {
114
+ return false ; // shouldn't happen
115
+ } ;
116
+ let path = if let QPath :: Resolved ( .., p) = q {
117
+ p
118
+ } else {
119
+ return false ; // give up
120
+ } ;
121
+ let var = adt_def. variant_of_res ( path. res ) ;
122
+ match & pat. kind {
123
+ PatKind :: TupleStruct ( _, params, _) => {
124
+ * r += & * snippet_with_applicability ( cx, path. span , ".." , app) ;
125
+ * r += "(" ;
126
+ for ( i, ( p, f) ) in params. iter ( ) . zip ( var. fields . iter ( ) ) . enumerate ( ) {
127
+ if i != 0 {
128
+ * r += ", " ;
129
+ }
130
+ inner ( cx, app, p, f. ty ( cx. tcx , generic_args) , r) ;
131
+ }
132
+ * r += ")" ;
133
+ } ,
134
+ PatKind :: Struct ( _, fields, _) => {
135
+ * r += & * snippet_with_applicability ( cx, path. span , ".." , app) ;
136
+ * r += " { " ;
137
+ for ( i, p) in fields. iter ( ) . enumerate ( ) {
138
+ if i != 0 {
139
+ * r += ", " ;
140
+ }
141
+ * r += & * snippet_with_applicability ( cx, p. ident . span , ".." , app) ;
142
+ * r += ": " ;
143
+ if let Some ( x) = var. fields . iter ( ) . find ( |f| f. ident == p. ident ) {
144
+ inner ( cx, app, p. pat , x. ty ( cx. tcx , generic_args) , r) ;
145
+ } else {
146
+ return false ; // won't happen
147
+ }
148
+ }
149
+ * r += " }" ;
150
+ } ,
151
+ _ => return false , // won't happen
152
+ }
153
+ } ,
154
+ _ => {
155
+ * r += & * snippet_with_applicability ( cx, pat. span , ".." , app) ;
156
+ } ,
157
+ }
158
+ return true ;
159
+ }
160
+ if goal. is_ref ( ) {
161
+ if let Some ( tam) = goal. builtin_deref ( true ) {
162
+ * r += "&" ;
163
+ return inner ( cx, app, pat, tam. ty , r) ;
164
+ }
165
+ }
166
+ false
167
+ }
168
+ let mut r = "" . to_string ( ) ;
169
+ if let PatKind :: Struct ( ..) = pat. kind {
170
+ r += "(" ;
171
+ }
172
+ let success = inner ( cx, app, pat, goal, & mut r) ;
173
+ if let PatKind :: Struct ( ..) = pat. kind {
174
+ r += ")" ;
175
+ }
176
+ if !success {
177
+ return None ;
178
+ }
179
+ Some ( r)
180
+ }
181
+
182
+ fn emit_lint ( cx : & LateContext < ' tcx > , pat : & Pat < ' _ > , exp : & Expr < ' _ > , span : Span , lint : & ' static Lint ) {
183
+ if_chain ! {
184
+ if equatable_pattern( cx, pat) ;
185
+ let exp_ty = cx. typeck_results( ) . expr_ty( exp) ;
186
+ if is_partial_eq( cx, exp_ty, exp_ty) ;
187
+ let mut app = Applicability :: MachineApplicable ;
188
+ if let Some ( pat_str) = pat_to_string( cx, & mut app, pat, exp_ty) ;
189
+ then {
190
+ /*let pat_str = match pat.kind {
191
+ PatKind::Struct(..) => format!(
192
+ "({})",
193
+ snippet_with_applicability(cx, pat.span, "..", &mut applicability),
194
+ ),
195
+ _ => snippet_with_applicability(cx, pat.span, "..", &mut applicability).to_string(),
196
+ };*/
197
+ let exp_str = snippet_with_applicability( cx, exp. span, ".." , & mut app) ;
198
+ span_lint_and_sugg(
199
+ cx,
200
+ lint,
201
+ span,
202
+ "this pattern matching can be expressed using equality" ,
203
+ "try" ,
204
+ format!(
205
+ "{} == {}" ,
206
+ exp_str,
207
+ pat_str,
208
+ ) ,
209
+ app,
210
+ ) ;
211
+ }
212
+ }
213
+ }
214
+
66
215
impl < ' tcx > LateLintPass < ' tcx > for PatternEquality {
67
216
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
68
- if_chain ! {
69
- if let ExprKind :: Let ( pat, exp, _) = expr. kind;
70
- if unary_pattern( pat) ;
71
- let exp_ty = cx. typeck_results( ) . expr_ty( exp) ;
72
- let pat_ty = cx. typeck_results( ) . pat_ty( pat) ;
73
- if is_structural_partial_eq( cx, exp_ty, pat_ty) ;
74
- then {
75
-
76
- let mut applicability = Applicability :: MachineApplicable ;
77
- let pat_str = match pat. kind {
78
- PatKind :: Struct ( ..) => format!(
79
- "({})" ,
80
- snippet_with_applicability( cx, pat. span, ".." , & mut applicability) ,
81
- ) ,
82
- _ => snippet_with_applicability( cx, pat. span, ".." , & mut applicability) . to_string( ) ,
83
- } ;
84
- span_lint_and_sugg(
85
- cx,
86
- EQUATABLE_IF_LET ,
87
- expr. span,
88
- "this pattern matching can be expressed using equality" ,
89
- "try" ,
90
- format!(
91
- "{} == {}" ,
92
- snippet_with_applicability( cx, exp. span, ".." , & mut applicability) ,
93
- pat_str,
94
- ) ,
95
- applicability,
96
- ) ;
97
- }
217
+ if let ExprKind :: Let ( pat, exp, _) = expr. kind {
218
+ emit_lint ( cx, pat, exp, expr. span , EQUATABLE_IF_LET ) ;
219
+ }
220
+ if let Some ( MatchesExpn {
221
+ call_site,
222
+ arm : Arm { pat, guard : None , .. } ,
223
+ exp,
224
+ } ) = MatchesExpn :: parse ( expr)
225
+ {
226
+ emit_lint ( cx, pat, exp, call_site, EQUATABLE_MATCHES ) ;
98
227
}
99
228
}
100
229
}
0 commit comments