Skip to content

Commit 5817a4f

Browse files
author
Michael Wright
committed
Add to_digit_is_some lint
1 parent 6f9c43d commit 5817a4f

File tree

8 files changed

+145
-2
lines changed

8 files changed

+145
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,6 +1203,7 @@ Released 2018-09-13
12031203
[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
12041204
[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
12051205
[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
1206+
[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
12061207
[`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
12071208
[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
12081209
[`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
88

9-
[There are 332 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
9+
[There are 333 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1010

1111
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1212

clippy_lints/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ pub mod strings;
272272
pub mod suspicious_trait_impl;
273273
pub mod swap;
274274
pub mod temporary_assignment;
275+
pub mod to_digit_is_some;
275276
pub mod trait_bounds;
276277
pub mod transmute;
277278
pub mod transmuting_null;
@@ -713,6 +714,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
713714
&swap::ALMOST_SWAPPED,
714715
&swap::MANUAL_SWAP,
715716
&temporary_assignment::TEMPORARY_ASSIGNMENT,
717+
&to_digit_is_some::TO_DIGIT_IS_SOME,
716718
&trait_bounds::TYPE_REPETITION_IN_BOUNDS,
717719
&transmute::CROSSPOINTER_TRANSMUTE,
718720
&transmute::TRANSMUTE_BYTES_TO_STR,
@@ -944,6 +946,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
944946
store.register_late_pass(|| box unused_self::UnusedSelf);
945947
store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall);
946948
store.register_late_pass(|| box exit::Exit);
949+
store.register_late_pass(|| box to_digit_is_some::ToDigitIsSome);
947950

948951
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
949952
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@@ -1238,6 +1241,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
12381241
LintId::of(&swap::ALMOST_SWAPPED),
12391242
LintId::of(&swap::MANUAL_SWAP),
12401243
LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT),
1244+
LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME),
12411245
LintId::of(&transmute::CROSSPOINTER_TRANSMUTE),
12421246
LintId::of(&transmute::TRANSMUTE_BYTES_TO_STR),
12431247
LintId::of(&transmute::TRANSMUTE_INT_TO_BOOL),
@@ -1363,6 +1367,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
13631367
LintId::of(&returns::NEEDLESS_RETURN),
13641368
LintId::of(&returns::UNUSED_UNIT),
13651369
LintId::of(&strings::STRING_LIT_AS_BYTES),
1370+
LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME),
13661371
LintId::of(&try_err::TRY_ERR),
13671372
LintId::of(&types::FN_TO_NUMERIC_CAST),
13681373
LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),

clippy_lints/src/to_digit_is_some.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
use crate::utils::{match_def_path, snippet_with_applicability, span_lint_and_sugg};
2+
use if_chain::if_chain;
3+
use rustc::hir;
4+
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
5+
use rustc::ty;
6+
use rustc::{declare_lint_pass, declare_tool_lint};
7+
use rustc_errors::Applicability;
8+
9+
declare_clippy_lint! {
10+
/// **What it does:** Checks for `.to_digit().is_some()` on `char`s.
11+
///
12+
/// **Why is this bad?** This is a convoluted way of checking if a `char` is a digit. It's
13+
/// more straight forward use the dedicated `is_digit` method.
14+
///
15+
/// **Example:**
16+
/// ```rust
17+
/// # let x: char = 'x'
18+
/// let is_digit = x.to_digit().is_some();
19+
/// ```
20+
/// can be written as:
21+
/// ```
22+
/// # let x: char = 'x'
23+
/// let is_digit = x.is_digit();
24+
/// ```
25+
pub TO_DIGIT_IS_SOME,
26+
style,
27+
"`char.is_digit()` is clearer"
28+
}
29+
30+
declare_lint_pass!(ToDigitIsSome => [TO_DIGIT_IS_SOME]);
31+
32+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ToDigitIsSome {
33+
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
34+
if_chain! {
35+
if let hir::ExprKind::MethodCall(is_some_path, _, is_some_args) = &expr.kind;
36+
if is_some_path.ident.name.as_str() == "is_some";
37+
if let [to_digit_expr] = &**is_some_args;
38+
then {
39+
let match_result = match &to_digit_expr.kind {
40+
hir::ExprKind::MethodCall(to_digits_path, _, to_digit_args) => {
41+
if_chain! {
42+
if let [char_arg, radix_arg] = &**to_digit_args;
43+
if to_digits_path.ident.name.as_str() == "to_digit";
44+
let char_arg_ty = cx.tables.expr_ty_adjusted(char_arg);
45+
if char_arg_ty.kind == ty::Char;
46+
then {
47+
Some((true, char_arg, radix_arg))
48+
} else {
49+
None
50+
}
51+
}
52+
}
53+
hir::ExprKind::Call(to_digits_call, to_digit_args) => {
54+
if_chain! {
55+
if let [char_arg, radix_arg] = &**to_digit_args;
56+
if let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind;
57+
if let to_digits_call_res = cx.tables.qpath_res(to_digits_path, to_digits_call.hir_id);
58+
if let Some(to_digits_def_id) = to_digits_call_res.opt_def_id();
59+
if match_def_path(cx, to_digits_def_id, &["core", "char", "methods", "<impl char>", "to_digit"]);
60+
then {
61+
Some((false, char_arg, radix_arg))
62+
} else {
63+
None
64+
}
65+
}
66+
}
67+
_ => None
68+
};
69+
70+
if let Some((is_method_call, char_arg, radix_arg)) = match_result {
71+
let mut applicability = Applicability::MachineApplicable;
72+
let char_arg_snip = snippet_with_applicability(cx, char_arg.span, "_", &mut applicability);
73+
let radix_snip = snippet_with_applicability(cx, radix_arg.span, "_", &mut applicability);
74+
75+
span_lint_and_sugg(
76+
cx,
77+
TO_DIGIT_IS_SOME,
78+
expr.span,
79+
"use of `.to_digit(..).is_some()`",
80+
"try this",
81+
if is_method_call {
82+
format!("{}.is_digit({})", char_arg_snip, radix_snip)
83+
} else {
84+
format!("char::is_digit({}, {})", char_arg_snip, radix_snip)
85+
},
86+
applicability,
87+
);
88+
}
89+
}
90+
}
91+
}
92+
}

src/lintlist/mod.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub use lint::Lint;
66
pub use lint::LINT_LEVELS;
77

88
// begin lint list, do not remove this comment, it’s used in `update_lints`
9-
pub const ALL_LINTS: [Lint; 332] = [
9+
pub const ALL_LINTS: [Lint; 333] = [
1010
Lint {
1111
name: "absurd_extreme_comparisons",
1212
group: "correctness",
@@ -1876,6 +1876,13 @@ pub const ALL_LINTS: [Lint; 332] = [
18761876
deprecation: None,
18771877
module: "methods",
18781878
},
1879+
Lint {
1880+
name: "to_digit_is_some",
1881+
group: "style",
1882+
desc: "`char.is_digit()` is clearer",
1883+
deprecation: None,
1884+
module: "to_digit_is_some",
1885+
},
18791886
Lint {
18801887
name: "todo",
18811888
group: "restriction",

tests/ui/to_digit_is_some.fixed

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//run-rustfix
2+
3+
#![warn(clippy::to_digit_is_some)]
4+
5+
fn main() {
6+
let c = 'x';
7+
let d = &c;
8+
9+
let _ = d.is_digit(10);
10+
let _ = char::is_digit(c, 10);
11+
}

tests/ui/to_digit_is_some.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//run-rustfix
2+
3+
#![warn(clippy::to_digit_is_some)]
4+
5+
fn main() {
6+
let c = 'x';
7+
let d = &c;
8+
9+
let _ = d.to_digit(10).is_some();
10+
let _ = char::to_digit(c, 10).is_some();
11+
}

tests/ui/to_digit_is_some.stderr

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: use of `.to_digit(..).is_some()`
2+
--> $DIR/to_digit_is_some.rs:9:13
3+
|
4+
LL | let _ = d.to_digit(10).is_some();
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `d.is_digit(10)`
6+
|
7+
= note: `-D clippy::to-digit-is-some` implied by `-D warnings`
8+
9+
error: use of `.to_digit(..).is_some()`
10+
--> $DIR/to_digit_is_some.rs:10:13
11+
|
12+
LL | let _ = char::to_digit(c, 10).is_some();
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `char::is_digit(c, 10)`
14+
15+
error: aborting due to 2 previous errors
16+

0 commit comments

Comments
 (0)