Skip to content

Commit 79d3b30

Browse files
committed
Auto merge of #4801 - mikerite:to_digit_is_some, r=flip1995
To digit is some Add a lint that recommends replacing `to_digit().is_some()` with `is_digit()` on `char`s changelog: Add lint `to_digit_is_some`
2 parents 338f5e6 + 89b966c commit 79d3b30

9 files changed

+150
-5
lines changed

CHANGELOG.md

+1
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

+1-1
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

+5
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ pub mod strings;
274274
pub mod suspicious_trait_impl;
275275
pub mod swap;
276276
pub mod temporary_assignment;
277+
pub mod to_digit_is_some;
277278
pub mod trait_bounds;
278279
pub mod transmute;
279280
pub mod transmuting_null;
@@ -715,6 +716,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
715716
&swap::ALMOST_SWAPPED,
716717
&swap::MANUAL_SWAP,
717718
&temporary_assignment::TEMPORARY_ASSIGNMENT,
719+
&to_digit_is_some::TO_DIGIT_IS_SOME,
718720
&trait_bounds::TYPE_REPETITION_IN_BOUNDS,
719721
&transmute::CROSSPOINTER_TRANSMUTE,
720722
&transmute::TRANSMUTE_BYTES_TO_STR,
@@ -946,6 +948,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
946948
store.register_late_pass(|| box unused_self::UnusedSelf);
947949
store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall);
948950
store.register_late_pass(|| box exit::Exit);
951+
store.register_late_pass(|| box to_digit_is_some::ToDigitIsSome);
949952

950953
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
951954
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@@ -1240,6 +1243,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
12401243
LintId::of(&swap::ALMOST_SWAPPED),
12411244
LintId::of(&swap::MANUAL_SWAP),
12421245
LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT),
1246+
LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME),
12431247
LintId::of(&transmute::CROSSPOINTER_TRANSMUTE),
12441248
LintId::of(&transmute::TRANSMUTE_BYTES_TO_STR),
12451249
LintId::of(&transmute::TRANSMUTE_INT_TO_BOOL),
@@ -1365,6 +1369,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
13651369
LintId::of(&returns::NEEDLESS_RETURN),
13661370
LintId::of(&returns::UNUSED_UNIT),
13671371
LintId::of(&strings::STRING_LIT_AS_BYTES),
1372+
LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME),
13681373
LintId::of(&try_err::TRY_ERR),
13691374
LintId::of(&types::FN_TO_NUMERIC_CAST),
13701375
LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),

clippy_lints/src/literal_representation.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ impl LiteralDigitGrouping {
364364
if_chain! {
365365
if let Some(src) = snippet_opt(cx, lit.span);
366366
if let Some(firstch) = src.chars().next();
367-
if char::to_digit(firstch, 10).is_some();
367+
if char::is_digit(firstch, 10);
368368
then {
369369
let digit_info = DigitInfo::new(&src, false);
370370
let _ = Self::do_lint(digit_info.digits, digit_info.suffix, in_macro).map_err(|warning_type| {
@@ -378,7 +378,7 @@ impl LiteralDigitGrouping {
378378
if_chain! {
379379
if let Some(src) = snippet_opt(cx, lit.span);
380380
if let Some(firstch) = src.chars().next();
381-
if char::to_digit(firstch, 10).is_some();
381+
if char::is_digit(firstch, 10);
382382
then {
383383
let digit_info = DigitInfo::new(&src, true);
384384
// Separate digits into integral and fractional parts.
@@ -512,7 +512,7 @@ impl DecimalLiteralRepresentation {
512512
if let LitKind::Int(val, _) = lit.kind;
513513
if let Some(src) = snippet_opt(cx, lit.span);
514514
if let Some(firstch) = src.chars().next();
515-
if char::to_digit(firstch, 10).is_some();
515+
if char::is_digit(firstch, 10);
516516
let digit_info = DigitInfo::new(&src, false);
517517
if digit_info.radix == Radix::Decimal;
518518
if val >= u128::from(self.threshold);

clippy_lints/src/to_digit_is_some.rs

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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 to use the dedicated `is_digit` method.
14+
///
15+
/// **Example:**
16+
/// ```rust
17+
/// # let c = 'c';
18+
/// # let radix = 10;
19+
/// let is_digit = c.to_digit(radix).is_some();
20+
/// ```
21+
/// can be written as:
22+
/// ```
23+
/// # let c = 'c';
24+
/// # let radix = 10;
25+
/// let is_digit = c.is_digit(radix);
26+
/// ```
27+
pub TO_DIGIT_IS_SOME,
28+
style,
29+
"`char.is_digit()` is clearer"
30+
}
31+
32+
declare_lint_pass!(ToDigitIsSome => [TO_DIGIT_IS_SOME]);
33+
34+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ToDigitIsSome {
35+
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
36+
if_chain! {
37+
if let hir::ExprKind::MethodCall(is_some_path, _, is_some_args) = &expr.kind;
38+
if is_some_path.ident.name.as_str() == "is_some";
39+
if let [to_digit_expr] = &**is_some_args;
40+
then {
41+
let match_result = match &to_digit_expr.kind {
42+
hir::ExprKind::MethodCall(to_digits_path, _, to_digit_args) => {
43+
if_chain! {
44+
if let [char_arg, radix_arg] = &**to_digit_args;
45+
if to_digits_path.ident.name.as_str() == "to_digit";
46+
let char_arg_ty = cx.tables.expr_ty_adjusted(char_arg);
47+
if char_arg_ty.kind == ty::Char;
48+
then {
49+
Some((true, char_arg, radix_arg))
50+
} else {
51+
None
52+
}
53+
}
54+
}
55+
hir::ExprKind::Call(to_digits_call, to_digit_args) => {
56+
if_chain! {
57+
if let [char_arg, radix_arg] = &**to_digit_args;
58+
if let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind;
59+
if let to_digits_call_res = cx.tables.qpath_res(to_digits_path, to_digits_call.hir_id);
60+
if let Some(to_digits_def_id) = to_digits_call_res.opt_def_id();
61+
if match_def_path(cx, to_digits_def_id, &["core", "char", "methods", "<impl char>", "to_digit"]);
62+
then {
63+
Some((false, char_arg, radix_arg))
64+
} else {
65+
None
66+
}
67+
}
68+
}
69+
_ => None
70+
};
71+
72+
if let Some((is_method_call, char_arg, radix_arg)) = match_result {
73+
let mut applicability = Applicability::MachineApplicable;
74+
let char_arg_snip = snippet_with_applicability(cx, char_arg.span, "_", &mut applicability);
75+
let radix_snip = snippet_with_applicability(cx, radix_arg.span, "_", &mut applicability);
76+
77+
span_lint_and_sugg(
78+
cx,
79+
TO_DIGIT_IS_SOME,
80+
expr.span,
81+
"use of `.to_digit(..).is_some()`",
82+
"try this",
83+
if is_method_call {
84+
format!("{}.is_digit({})", char_arg_snip, radix_snip)
85+
} else {
86+
format!("char::is_digit({}, {})", char_arg_snip, radix_snip)
87+
},
88+
applicability,
89+
);
90+
}
91+
}
92+
}
93+
}
94+
}

src/lintlist/mod.rs

+8-1
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

+11
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

+11
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

+16
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)