@@ -2,15 +2,17 @@ use std::cmp::Ordering;
22use std:: ops:: ControlFlow ;
33
44use 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;
78use clippy_utils:: visitors:: { for_each_expr, Descend } ;
89use rustc_ast:: { BinOpKind , LitKind } ;
910use rustc_errors:: Applicability ;
1011use rustc_hir:: { Expr , ExprKind , PatKind , QPath } ;
1112use rustc_lint:: { LateContext , LateLintPass } ;
1213use rustc_middle:: ty;
1314use rustc_session:: declare_lint_pass;
15+ use rustc_span:: sym;
1416
1517declare_clippy_lint ! {
1618 /// ### What it does
@@ -93,95 +95,110 @@ const PATTERN_METHODS: [(&str, usize); 22] = [
9395 ( "replacen" , 0 ) ,
9496] ;
9597
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 ) {
98101 span_lint_and_sugg (
99102 cx,
100103 SINGLE_CHAR_PATTERN ,
101104 arg. span ,
102105 "single-character string constant used as pattern" ,
103106 "consider using a `char`" ,
104107 hint,
105- * applicability,
108+ applicability,
106109 ) ;
107110 }
108111}
109112
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
112138 && 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
114140 {
115- let ex = body. value ;
116141 let mut set_chars: Vec < String > = Vec :: new ( ) ;
117142
118143 // We want to retrieve all the comparisons done.
119144 // 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 > {
121146 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 )
137160 } else {
138161 ControlFlow :: Break ( ( ) )
139162 }
140163 } ,
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 ( ) {
143167 return ControlFlow :: Break ( ( ) ) ;
144168 }
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 ( ( ) ) ;
159171 }
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 ( ( ) ) ;
165174 }
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 ( ( ) )
174188 }
175- ControlFlow :: Continue ( Descend :: No )
176189 } ,
177- _ => ControlFlow :: Continue ( Descend :: No ) ,
190+ _ => ControlFlow :: Break ( ( ) ) ,
178191 }
179- } ) ;
192+ } )
193+ . is_some ( )
194+ {
195+ return ;
196+ }
180197 match set_chars. len ( ) . cmp ( & 1 ) {
181198 Ordering :: Equal => span_lint_and_sugg (
182199 cx,
183200 MANUAL_PATTERN_CHAR_COMPARISON ,
184- arg . span ,
201+ method_arg . span ,
185202 "this manual char comparison can be written more succinctly" ,
186203 "consider using a `char`" ,
187204 set_chars[ 0 ] . clone ( ) ,
@@ -190,7 +207,7 @@ fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, arg: &Expr<'_>, ap
190207 Ordering :: Greater => span_lint_and_sugg (
191208 cx,
192209 MANUAL_PATTERN_CHAR_COMPARISON ,
193- arg . span ,
210+ method_arg . span ,
194211 "this manual char comparison can be written more succinctly" ,
195212 "consider using an array of `char`" ,
196213 format ! ( "[{}]" , set_chars. join( ", " ) ) ,
@@ -214,10 +231,9 @@ impl<'tcx> LateLintPass<'tcx> for StringPatterns {
214231 && args. len ( ) > pos
215232 {
216233 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) ;
219235
220- check_manual_pattern_char_comparison ( cx, arg, applicability ) ;
236+ check_manual_pattern_char_comparison ( cx, arg) ;
221237 }
222238 }
223239}
0 commit comments