1
1
use clippy_utils:: msrvs:: { self , Msrv } ;
2
- use clippy_utils:: { diagnostics:: span_lint_and_sugg, in_constant, macros:: root_macro_call, source:: snippet} ;
2
+ use clippy_utils:: { diagnostics:: span_lint_and_sugg, higher, in_constant, macros:: root_macro_call, source:: snippet} ;
3
+ use rustc_ast:: ast:: RangeLimits ;
3
4
use rustc_ast:: LitKind :: { Byte , Char } ;
4
5
use rustc_errors:: Applicability ;
5
- use rustc_hir:: { Expr , ExprKind , PatKind , RangeEnd } ;
6
+ use rustc_hir:: { BorrowKind , Expr , ExprKind , PatKind , RangeEnd } ;
6
7
use rustc_lint:: { LateContext , LateLintPass } ;
7
8
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
8
- use rustc_span:: { def_id:: DefId , sym} ;
9
+ use rustc_span:: { def_id:: DefId , sym, Span } ;
9
10
10
11
declare_clippy_lint ! {
11
12
/// ### What it does
@@ -23,6 +24,10 @@ declare_clippy_lint! {
23
24
/// assert!(matches!(b'X', b'A'..=b'Z'));
24
25
/// assert!(matches!('2', '0'..='9'));
25
26
/// assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
27
+ ///
28
+ /// ('0'..='9').contains(&'0');
29
+ /// ('a'..='z').contains(&'a');
30
+ /// ('A'..='Z').contains(&'A');
26
31
/// }
27
32
/// ```
28
33
/// Use instead:
@@ -32,6 +37,10 @@ declare_clippy_lint! {
32
37
/// assert!(b'X'.is_ascii_uppercase());
33
38
/// assert!('2'.is_ascii_digit());
34
39
/// assert!('x'.is_ascii_alphabetic());
40
+ ///
41
+ /// '0'.is_ascii_digit();
42
+ /// 'a'.is_ascii_lowercase();
43
+ /// 'A'.is_ascii_uppercase();
35
44
/// }
36
45
/// ```
37
46
#[ clippy:: version = "1.66.0" ]
@@ -75,47 +84,59 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
75
84
return ;
76
85
}
77
86
78
- let Some ( macro_call) = root_macro_call ( expr. span ) else { return } ;
79
-
80
- if is_matches_macro ( cx, macro_call. def_id ) {
87
+ if let Some ( macro_call) = root_macro_call ( expr. span )
88
+ && is_matches_macro ( cx, macro_call. def_id ) {
81
89
if let ExprKind :: Match ( recv, [ arm, ..] , _) = expr. kind {
82
90
let range = check_pat ( & arm. pat . kind ) ;
83
-
84
- if let Some ( sugg) = match range {
85
- CharRange :: UpperChar => Some ( "is_ascii_uppercase" ) ,
86
- CharRange :: LowerChar => Some ( "is_ascii_lowercase" ) ,
87
- CharRange :: FullChar => Some ( "is_ascii_alphabetic" ) ,
88
- CharRange :: Digit => Some ( "is_ascii_digit" ) ,
89
- CharRange :: Otherwise => None ,
90
- } {
91
- let default_snip = ".." ;
92
- // `snippet_with_applicability` may set applicability to `MaybeIncorrect` for
93
- // macro span, so we check applicability manually by comparing `recv` is not default.
94
- let recv = snippet ( cx, recv. span , default_snip) ;
95
-
96
- let applicability = if recv == default_snip {
97
- Applicability :: HasPlaceholders
98
- } else {
99
- Applicability :: MachineApplicable
100
- } ;
101
-
102
- span_lint_and_sugg (
103
- cx,
104
- MANUAL_IS_ASCII_CHECK ,
105
- macro_call. span ,
106
- "manual check for common ascii range" ,
107
- "try" ,
108
- format ! ( "{recv}.{sugg}()" ) ,
109
- applicability,
110
- ) ;
111
- }
91
+ check_is_ascii ( cx, macro_call. span , recv, & range) ;
92
+ }
93
+ } else if let ExprKind :: MethodCall ( path, receiver, [ arg] , ..) = expr. kind
94
+ && path. ident . name == sym ! ( contains)
95
+ && let Some ( higher:: Range { start : Some ( start) , end : Some ( end) , limits : RangeLimits :: Closed } )
96
+ = higher:: Range :: hir ( receiver) {
97
+ let range = check_range ( start, end) ;
98
+ if let ExprKind :: AddrOf ( BorrowKind :: Ref , _, e) = arg. kind {
99
+ check_is_ascii ( cx, expr. span , e, & range) ;
100
+ } else {
101
+ check_is_ascii ( cx, expr. span , arg, & range) ;
112
102
}
113
103
}
114
104
}
115
105
116
106
extract_msrv_attr ! ( LateContext ) ;
117
107
}
118
108
109
+ fn check_is_ascii ( cx : & LateContext < ' _ > , span : Span , recv : & Expr < ' _ > , range : & CharRange ) {
110
+ if let Some ( sugg) = match range {
111
+ CharRange :: UpperChar => Some ( "is_ascii_uppercase" ) ,
112
+ CharRange :: LowerChar => Some ( "is_ascii_lowercase" ) ,
113
+ CharRange :: FullChar => Some ( "is_ascii_alphabetic" ) ,
114
+ CharRange :: Digit => Some ( "is_ascii_digit" ) ,
115
+ CharRange :: Otherwise => None ,
116
+ } {
117
+ let default_snip = ".." ;
118
+ // `snippet_with_applicability` may set applicability to `MaybeIncorrect` for
119
+ // macro span, so we check applicability manually by comparing `recv` is not default.
120
+ let recv = snippet ( cx, recv. span , default_snip) ;
121
+
122
+ let applicability = if recv == default_snip {
123
+ Applicability :: HasPlaceholders
124
+ } else {
125
+ Applicability :: MachineApplicable
126
+ } ;
127
+
128
+ span_lint_and_sugg (
129
+ cx,
130
+ MANUAL_IS_ASCII_CHECK ,
131
+ span,
132
+ "manual check for common ascii range" ,
133
+ "try" ,
134
+ format ! ( "{recv}.{sugg}()" ) ,
135
+ applicability,
136
+ ) ;
137
+ }
138
+ }
139
+
119
140
fn check_pat ( pat_kind : & PatKind < ' _ > ) -> CharRange {
120
141
match pat_kind {
121
142
PatKind :: Or ( pats) => {
0 commit comments