@@ -2,15 +2,17 @@ use std::cmp::Ordering;
2
2
use std:: ops:: ControlFlow ;
3
3
4
4
use clippy_utils:: diagnostics:: span_lint_and_sugg;
5
- use clippy_utils:: source:: snippet;
6
- use clippy_utils:: str_utils:: get_hint_if_single_char_arg;
5
+ use clippy_utils:: macros:: matching_root_macro_call;
6
+ use clippy_utils:: path_to_local_id;
7
+ use clippy_utils:: source:: str_literal_to_char_literal;
7
8
use clippy_utils:: visitors:: { for_each_expr, Descend } ;
8
9
use rustc_ast:: { BinOpKind , LitKind } ;
9
10
use rustc_errors:: Applicability ;
10
11
use rustc_hir:: { Expr , ExprKind , PatKind , QPath } ;
11
12
use rustc_lint:: { LateContext , LateLintPass } ;
12
13
use rustc_middle:: ty;
13
14
use rustc_session:: declare_lint_pass;
15
+ use rustc_span:: sym;
14
16
15
17
declare_clippy_lint ! {
16
18
/// ### What it does
@@ -93,95 +95,110 @@ const PATTERN_METHODS: [(&str, usize); 22] = [
93
95
( "replacen" , 0 ) ,
94
96
] ;
95
97
96
- fn check_single_char_pattern_lint ( cx : & LateContext < ' _ > , arg : & Expr < ' _ > , applicability : & mut Applicability ) {
97
- if let Some ( hint) = get_hint_if_single_char_arg ( cx, arg, applicability, true ) {
98
+ fn check_single_char_pattern_lint ( cx : & LateContext < ' _ > , arg : & Expr < ' _ > ) {
99
+ let mut applicability = Applicability :: MachineApplicable ;
100
+ if let Some ( hint) = str_literal_to_char_literal ( cx, arg, & mut applicability, true ) {
98
101
span_lint_and_sugg (
99
102
cx,
100
103
SINGLE_CHAR_PATTERN ,
101
104
arg. span ,
102
105
"single-character string constant used as pattern" ,
103
106
"consider using a `char`" ,
104
107
hint,
105
- * applicability,
108
+ applicability,
106
109
) ;
107
110
}
108
111
}
109
112
110
- fn check_manual_pattern_char_comparison ( cx : & LateContext < ' _ > , arg : & Expr < ' _ > , applicability : Applicability ) {
111
- if let ExprKind :: Closure ( closure) = arg. kind
113
+ fn get_char_value ( expr : & Expr < ' _ > ) -> Option < String > {
114
+ match expr. kind {
115
+ ExprKind :: Lit ( lit) => match lit. node {
116
+ LitKind :: Char ( c) => Some ( format ! ( "'{}'" , c. escape_default( ) ) ) ,
117
+ _ => None ,
118
+ } ,
119
+ ExprKind :: Path ( QPath :: Resolved ( _, path) ) => {
120
+ if path. segments . len ( ) == 1 {
121
+ let segment = & path. segments [ 0 ] ;
122
+ if segment. args . is_none ( ) {
123
+ Some ( segment. ident . name . to_string ( ) )
124
+ } else {
125
+ None
126
+ }
127
+ } else {
128
+ None
129
+ }
130
+ } ,
131
+ _ => None ,
132
+ }
133
+ }
134
+
135
+ fn check_manual_pattern_char_comparison ( cx : & LateContext < ' _ > , method_arg : & Expr < ' _ > ) {
136
+ let applicability = Applicability :: MachineApplicable ;
137
+ if let ExprKind :: Closure ( closure) = method_arg. kind
112
138
&& let body = cx. tcx . hir ( ) . body ( closure. body )
113
- && let Some ( param_name ) = body. params [ 0 ] . pat . simple_ident ( )
139
+ && let PatKind :: Binding ( _ , binding , .. ) = body. params [ 0 ] . pat . kind
114
140
{
115
- let ex = body. value ;
116
141
let mut set_chars: Vec < String > = Vec :: new ( ) ;
117
142
118
143
// We want to retrieve all the comparisons done.
119
144
// They are ordered in a nested way and so we need to traverse the AST to collect them all.
120
- for_each_expr ( ex , |sub_expr| -> ControlFlow < ( ) , Descend > {
145
+ if for_each_expr ( body . value , |sub_expr| -> ControlFlow < ( ) , Descend > {
121
146
match sub_expr. kind {
122
- ExprKind :: Binary ( op, left, right) => {
123
- // Avoid matching code that contains complex comparisons such as functions calls etc..
124
- // (ex: c.is_whitespace())
125
- if matches ! (
126
- left. kind,
127
- ExprKind :: Binary ( _, _, _) | ExprKind :: Match ( _, _, _) | ExprKind :: Lit ( _) | ExprKind :: Path ( _)
128
- ) && matches ! (
129
- right. kind,
130
- ExprKind :: Binary ( _, _, _) | ExprKind :: Match ( _, _, _) | ExprKind :: Lit ( _) | ExprKind :: Path ( _)
131
- ) {
132
- if matches ! ( op. node, BinOpKind :: Eq | BinOpKind :: Or ) {
133
- ControlFlow :: Continue ( Descend :: Yes )
134
- } else {
135
- ControlFlow :: Continue ( Descend :: No )
136
- }
147
+ ExprKind :: Binary ( op, left, right) if op. node == BinOpKind :: Eq => {
148
+ if path_to_local_id ( left, binding)
149
+ && cx. typeck_results ( ) . expr_ty_adjusted ( right) . kind ( ) == & ty:: Char
150
+ && let Some ( c) = get_char_value ( right)
151
+ {
152
+ set_chars. push ( c) ;
153
+ ControlFlow :: Continue ( Descend :: No )
154
+ } else if path_to_local_id ( right, binding)
155
+ && cx. typeck_results ( ) . expr_ty_adjusted ( left) . kind ( ) == & ty:: Char
156
+ && let Some ( c) = get_char_value ( left)
157
+ {
158
+ set_chars. push ( c) ;
159
+ ControlFlow :: Continue ( Descend :: No )
137
160
} else {
138
161
ControlFlow :: Break ( ( ) )
139
162
}
140
163
} ,
141
- ExprKind :: Match ( match_value, arms, _) => {
142
- if snippet ( cx, match_value. span , "" ) != param_name. name . as_str ( ) || arms. len ( ) != 2 {
164
+ ExprKind :: Binary ( op, _, _) if op. node == BinOpKind :: Or => ControlFlow :: Continue ( Descend :: Yes ) ,
165
+ ExprKind :: Match ( match_value, [ arm, _] , _) => {
166
+ if matching_root_macro_call ( cx, sub_expr. span , sym:: matches_macro) . is_none ( ) {
143
167
return ControlFlow :: Break ( ( ) ) ;
144
168
}
145
- if let PatKind :: Or ( pats) = arms[ 0 ] . pat . kind {
146
- for pat in pats {
147
- if let PatKind :: Lit ( expr) = pat. kind
148
- && let ExprKind :: Lit ( lit) = expr. kind
149
- && let LitKind :: Char ( c) = lit. node
150
- {
151
- set_chars. push ( format ! ( "'{}'" , c. escape_default( ) ) ) ;
152
- }
153
- }
154
- } else if let PatKind :: Lit ( expr) = arms[ 0 ] . pat . kind
155
- && let ExprKind :: Lit ( lit) = expr. kind
156
- && let LitKind :: Char ( c) = lit. node
157
- {
158
- set_chars. push ( format ! ( "'{}'" , c. escape_default( ) ) ) ;
169
+ if arm. guard . is_some ( ) {
170
+ return ControlFlow :: Break ( ( ) ) ;
159
171
}
160
- ControlFlow :: Continue ( Descend :: No )
161
- } ,
162
- ExprKind :: Lit ( lit) => {
163
- if let LitKind :: Char ( c) = lit. node {
164
- set_chars. push ( format ! ( "'{}'" , c. escape_default( ) ) ) ;
172
+ if !path_to_local_id ( match_value, binding) {
173
+ return ControlFlow :: Break ( ( ) ) ;
165
174
}
166
- ControlFlow :: Continue ( Descend :: No )
167
- } ,
168
- ExprKind :: Path ( QPath :: Resolved ( _, path) ) => {
169
- if path. segments . len ( ) == 1 {
170
- let segment = & path. segments [ 0 ] ;
171
- if segment. ident != param_name {
172
- set_chars. push ( segment. ident . to_string ( ) ) ;
173
- }
175
+ if arm. pat . walk_short ( |pat| match pat. kind {
176
+ PatKind :: Lit ( expr) if let ExprKind :: Lit ( lit) = expr. kind => {
177
+ if let LitKind :: Char ( c) = lit. node {
178
+ set_chars. push ( format ! ( "'{}'" , c. escape_default( ) ) ) ;
179
+ }
180
+ true
181
+ } ,
182
+ PatKind :: Or ( _) => true ,
183
+ _ => false ,
184
+ } ) {
185
+ ControlFlow :: Continue ( Descend :: No )
186
+ } else {
187
+ ControlFlow :: Break ( ( ) )
174
188
}
175
- ControlFlow :: Continue ( Descend :: No )
176
189
} ,
177
- _ => ControlFlow :: Continue ( Descend :: No ) ,
190
+ _ => ControlFlow :: Break ( ( ) ) ,
178
191
}
179
- } ) ;
192
+ } )
193
+ . is_some ( )
194
+ {
195
+ return ;
196
+ }
180
197
match set_chars. len ( ) . cmp ( & 1 ) {
181
198
Ordering :: Equal => span_lint_and_sugg (
182
199
cx,
183
200
MANUAL_PATTERN_CHAR_COMPARISON ,
184
- arg . span ,
201
+ method_arg . span ,
185
202
"this manual char comparison can be written more succinctly" ,
186
203
"consider using a `char`" ,
187
204
set_chars[ 0 ] . clone ( ) ,
@@ -190,7 +207,7 @@ fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, arg: &Expr<'_>, ap
190
207
Ordering :: Greater => span_lint_and_sugg (
191
208
cx,
192
209
MANUAL_PATTERN_CHAR_COMPARISON ,
193
- arg . span ,
210
+ method_arg . span ,
194
211
"this manual char comparison can be written more succinctly" ,
195
212
"consider using an array of `char`" ,
196
213
format ! ( "[{}]" , set_chars. join( ", " ) ) ,
@@ -214,10 +231,9 @@ impl<'tcx> LateLintPass<'tcx> for StringPatterns {
214
231
&& args. len ( ) > pos
215
232
{
216
233
let arg = & args[ pos] ;
217
- let mut applicability = Applicability :: MachineApplicable ;
218
- check_single_char_pattern_lint ( cx, arg, & mut applicability) ;
234
+ check_single_char_pattern_lint ( cx, arg) ;
219
235
220
- check_manual_pattern_char_comparison ( cx, arg, applicability ) ;
236
+ check_manual_pattern_char_comparison ( cx, arg) ;
221
237
}
222
238
}
223
239
}
0 commit comments