Skip to content

Commit 25c9718

Browse files
committed
check ranges with .contains calls
1 parent 911864d commit 25c9718

File tree

4 files changed

+54
-39
lines changed

4 files changed

+54
-39
lines changed

clippy_lints/src/manual_is_ascii_check.rs

Lines changed: 41 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
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};
33
use rustc_ast::LitKind::{Byte, Char};
44
use rustc_errors::Applicability;
55
use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd};
66
use rustc_lint::{LateContext, LateLintPass};
77
use rustc_session::{declare_tool_lint, impl_lint_pass};
8-
use rustc_span::{def_id::DefId, sym};
8+
use rustc_span::{def_id::DefId, sym, Span};
99

1010
declare_clippy_lint! {
1111
/// ### What it does
@@ -75,47 +75,54 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
7575
return;
7676
}
7777

78-
let Some(macro_call) = root_macro_call(expr.span) else { return };
79-
80-
if is_matches_macro(cx, macro_call.def_id) {
78+
if let Some(macro_call) = root_macro_call(expr.span)
79+
&& is_matches_macro(cx, macro_call.def_id) {
8180
if let ExprKind::Match(recv, [arm, ..], _) = expr.kind {
8281
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-
}
82+
check_is_ascii(cx, macro_call.span, recv, &range);
11283
}
84+
} else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind
85+
&& path.ident.name == sym!(contains)
86+
&& let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(receiver) {
87+
let range = check_range(start, end);
88+
check_is_ascii(cx, expr.span, arg, &range);
11389
}
11490
}
11591

11692
extract_msrv_attr!(LateContext);
11793
}
11894

95+
fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &CharRange) {
96+
if let Some(sugg) = match range {
97+
CharRange::UpperChar => Some("is_ascii_uppercase"),
98+
CharRange::LowerChar => Some("is_ascii_lowercase"),
99+
CharRange::FullChar => Some("is_ascii_alphabetic"),
100+
CharRange::Digit => Some("is_ascii_digit"),
101+
CharRange::Otherwise => None,
102+
} {
103+
let default_snip = "..";
104+
// `snippet_with_applicability` may set applicability to `MaybeIncorrect` for
105+
// macro span, so we check applicability manually by comparing `recv` is not default.
106+
let recv = snippet(cx, recv.span, default_snip);
107+
108+
let applicability = if recv == default_snip {
109+
Applicability::HasPlaceholders
110+
} else {
111+
Applicability::MachineApplicable
112+
};
113+
114+
span_lint_and_sugg(
115+
cx,
116+
MANUAL_IS_ASCII_CHECK,
117+
span,
118+
"manual check for common ascii range",
119+
"try",
120+
format!("{recv}.{sugg}()"),
121+
applicability,
122+
);
123+
}
124+
}
125+
119126
fn check_pat(pat_kind: &PatKind<'_>) -> CharRange {
120127
match pat_kind {
121128
PatKind::Or(pats) => {

tests/ui/manual_is_ascii_check.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ fn main() {
1515
assert!('x'.is_ascii_alphabetic());
1616

1717
assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_'));
18+
assert!(&b'0'.is_ascii_digit());
1819
}
1920

2021
#[clippy::msrv = "1.23"]

tests/ui/manual_is_ascii_check.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ fn main() {
1515
assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
1616

1717
assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_'));
18+
assert!((b'0'..=b'9').contains(&b'0'));
1819
}
1920

2021
#[clippy::msrv = "1.23"]

tests/ui/manual_is_ascii_check.stderr

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,28 +43,34 @@ LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
4343
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()`
4444

4545
error: manual check for common ascii range
46-
--> $DIR/manual_is_ascii_check.rs:29:13
46+
--> $DIR/manual_is_ascii_check.rs:18:13
47+
|
48+
LL | assert!((b'0'..=b'9').contains(&b'0'));
49+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&b'0'.is_ascii_digit()`
50+
51+
error: manual check for common ascii range
52+
--> $DIR/manual_is_ascii_check.rs:30:13
4753
|
4854
LL | assert!(matches!(b'1', b'0'..=b'9'));
4955
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()`
5056

5157
error: manual check for common ascii range
52-
--> $DIR/manual_is_ascii_check.rs:30:13
58+
--> $DIR/manual_is_ascii_check.rs:31:13
5359
|
5460
LL | assert!(matches!('X', 'A'..='Z'));
5561
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()`
5662

5763
error: manual check for common ascii range
58-
--> $DIR/manual_is_ascii_check.rs:31:13
64+
--> $DIR/manual_is_ascii_check.rs:32:13
5965
|
6066
LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
6167
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()`
6268

6369
error: manual check for common ascii range
64-
--> $DIR/manual_is_ascii_check.rs:41:23
70+
--> $DIR/manual_is_ascii_check.rs:42:23
6571
|
6672
LL | const FOO: bool = matches!('x', '0'..='9');
6773
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_digit()`
6874

69-
error: aborting due to 11 previous errors
75+
error: aborting due to 12 previous errors
7076

0 commit comments

Comments
 (0)