1- use clippy_utils:: diagnostics:: span_lint_and_sugg;
21use clippy_utils:: source:: snippet_with_applicability;
32use clippy_utils:: ty:: implements_trait;
3+ use clippy_utils:: { diagnostics:: span_lint_and_sugg, higher:: MatchesExpn } ;
44use if_chain:: if_chain;
55use 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 } ;
912use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
13+ use rustc_span:: Span ;
1014
1115declare_clippy_lint ! {
1216 /// ### 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>`.
1419 ///
1520 /// ### Why is this bad?
1621 ///
@@ -33,68 +38,192 @@ declare_clippy_lint! {
3338 /// }
3439 /// ```
3540 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 "
3843}
3944
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 ] ) ;
4168
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) )
4672 }
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 {
4978 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) ,
5090 } ,
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 ,
5594 }
5695}
5796
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 {
5998 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 ( ) ] )
61100 } else {
62101 false
63102 }
64103}
65104
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+
66215impl < ' tcx > LateLintPass < ' tcx > for PatternEquality {
67216 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 ) ;
98227 }
99228 }
100229}
0 commit comments