1
1
use clippy_config:: msrvs:: { self , Msrv } ;
2
- use clippy_utils:: diagnostics:: span_lint_and_sugg ;
2
+ use clippy_utils:: diagnostics:: span_lint_and_then ;
3
3
use clippy_utils:: macros:: matching_root_macro_call;
4
4
use clippy_utils:: sugg:: Sugg ;
5
- use clippy_utils:: { higher, in_constant} ;
5
+ use clippy_utils:: { higher, in_constant, path_to_local , peel_ref_operators } ;
6
6
use rustc_ast:: ast:: RangeLimits ;
7
7
use rustc_ast:: LitKind :: { Byte , Char } ;
8
8
use rustc_errors:: Applicability ;
9
- use rustc_hir:: { BorrowKind , Expr , ExprKind , PatKind , RangeEnd } ;
9
+ use rustc_hir:: { Expr , ExprKind , Node , Param , PatKind , RangeEnd } ;
10
10
use rustc_lint:: { LateContext , LateLintPass } ;
11
+ use rustc_middle:: ty;
11
12
use rustc_session:: impl_lint_pass;
12
13
use rustc_span:: { sym, Span } ;
13
14
@@ -99,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
99
100
if let Some ( macro_call) = matching_root_macro_call ( cx, expr. span , sym:: matches_macro) {
100
101
if let ExprKind :: Match ( recv, [ arm, ..] , _) = expr. kind {
101
102
let range = check_pat ( & arm. pat . kind ) ;
102
- check_is_ascii ( cx, macro_call. span , recv, & range) ;
103
+ check_is_ascii ( cx, macro_call. span , recv, & range, None ) ;
103
104
}
104
105
} else if let ExprKind :: MethodCall ( path, receiver, [ arg] , ..) = expr. kind
105
106
&& path. ident . name == sym ! ( contains)
@@ -108,42 +109,67 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
108
109
end : Some ( end) ,
109
110
limits : RangeLimits :: Closed ,
110
111
} ) = higher:: Range :: hir ( receiver)
112
+ && !matches ! ( cx. typeck_results( ) . expr_ty( arg) . peel_refs( ) . kind( ) , ty:: Param ( _) )
111
113
{
114
+ let arg = peel_ref_operators ( cx, arg) ;
115
+ let ty_sugg = get_ty_sugg ( cx, arg, start) ;
112
116
let range = check_range ( start, end) ;
113
- if let ExprKind :: AddrOf ( BorrowKind :: Ref , _, e) = arg. kind {
114
- check_is_ascii ( cx, expr. span , e, & range) ;
115
- } else {
116
- check_is_ascii ( cx, expr. span , arg, & range) ;
117
- }
117
+ check_is_ascii ( cx, expr. span , arg, & range, ty_sugg) ;
118
118
}
119
119
}
120
120
121
121
extract_msrv_attr ! ( LateContext ) ;
122
122
}
123
123
124
- fn check_is_ascii ( cx : & LateContext < ' _ > , span : Span , recv : & Expr < ' _ > , range : & CharRange ) {
125
- if let Some ( sugg) = match range {
126
- CharRange :: UpperChar => Some ( "is_ascii_uppercase" ) ,
127
- CharRange :: LowerChar => Some ( "is_ascii_lowercase" ) ,
128
- CharRange :: FullChar => Some ( "is_ascii_alphabetic" ) ,
129
- CharRange :: Digit => Some ( "is_ascii_digit" ) ,
130
- CharRange :: HexDigit => Some ( "is_ascii_hexdigit" ) ,
131
- CharRange :: Otherwise | CharRange :: LowerHexLetter | CharRange :: UpperHexLetter => None ,
132
- } {
133
- let default_snip = ".." ;
134
- let mut app = Applicability :: MachineApplicable ;
135
- let recv = Sugg :: hir_with_context ( cx, recv, span. ctxt ( ) , default_snip, & mut app) . maybe_par ( ) ;
124
+ fn get_ty_sugg ( cx : & LateContext < ' _ > , arg : & Expr < ' _ > , bound_expr : & Expr < ' _ > ) -> Option < ( Span , & ' static str ) > {
125
+ if let ExprKind :: Lit ( lit) = bound_expr. kind
126
+ && let local_hid = path_to_local ( arg) ?
127
+ && let Node :: Param ( Param { ty_span, span, .. } ) = cx. tcx . parent_hir_node ( local_hid)
128
+ // `ty_span` and `span` are the same for inferred type, thus a type suggestion must be given
129
+ && ty_span == span
130
+ {
131
+ let ty_str = match lit. node {
132
+ Char ( _) => "char" ,
133
+ Byte ( _) => "u8" ,
134
+ _ => return None ,
135
+ } ;
136
+ return Some ( ( * ty_span, ty_str) ) ;
137
+ }
138
+ None
139
+ }
136
140
137
- span_lint_and_sugg (
138
- cx,
139
- MANUAL_IS_ASCII_CHECK ,
140
- span,
141
- "manual check for common ascii range" ,
142
- "try" ,
143
- format ! ( "{recv}.{sugg}()" ) ,
144
- app,
145
- ) ;
141
+ fn check_is_ascii (
142
+ cx : & LateContext < ' _ > ,
143
+ span : Span ,
144
+ recv : & Expr < ' _ > ,
145
+ range : & CharRange ,
146
+ ty_sugg : Option < ( Span , & ' _ str ) > ,
147
+ ) {
148
+ let sugg = match range {
149
+ CharRange :: UpperChar => "is_ascii_uppercase" ,
150
+ CharRange :: LowerChar => "is_ascii_lowercase" ,
151
+ CharRange :: FullChar => "is_ascii_alphabetic" ,
152
+ CharRange :: Digit => "is_ascii_digit" ,
153
+ CharRange :: HexDigit => "is_ascii_hexdigit" ,
154
+ CharRange :: Otherwise | CharRange :: LowerHexLetter | CharRange :: UpperHexLetter => return ,
155
+ } ;
156
+ let default_snip = ".." ;
157
+ let mut app = Applicability :: MachineApplicable ;
158
+ let recv = Sugg :: hir_with_context ( cx, recv, span. ctxt ( ) , default_snip, & mut app) . maybe_par ( ) ;
159
+ let mut suggestion = vec ! [ ( span, format!( "{recv}.{sugg}()" ) ) ] ;
160
+ if let Some ( ( ty_span, ty_str) ) = ty_sugg {
161
+ suggestion. push ( ( ty_span, format ! ( "{recv}: {ty_str}" ) ) ) ;
146
162
}
163
+
164
+ span_lint_and_then (
165
+ cx,
166
+ MANUAL_IS_ASCII_CHECK ,
167
+ span,
168
+ "manual check for common ascii range" ,
169
+ |diag| {
170
+ diag. multipart_suggestion ( "try" , suggestion, app) ;
171
+ } ,
172
+ ) ;
147
173
}
148
174
149
175
fn check_pat ( pat_kind : & PatKind < ' _ > ) -> CharRange {
0 commit comments