Auto merge of #8624 - pitaj:is_digit_ascii_radix, r=xFrednet

New lint `is_digit_ascii_radix`

Closes #6399

changelog: Added [`is_digit_ascii_radix`]: recommend `is_ascii_digit()` or `is_ascii_hexdigit()` in place of `is_digit(10)` and `is_digit(16)`
This commit is contained in:
bors 2022-04-11 18:56:21 +00:00
commit dbcd82885f
18 changed files with 160 additions and 14 deletions

View file

@ -0,0 +1,50 @@
//! Lint for `c.is_digit(10)`
use super::IS_DIGIT_ASCII_RADIX;
use clippy_utils::{
consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, meets_msrv, msrvs,
source::snippet_with_applicability,
};
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_semver::RustcVersion;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
self_arg: &'tcx Expr<'_>,
radix: &'tcx Expr<'_>,
msrv: Option<&RustcVersion>,
) {
if !meets_msrv(msrv, &msrvs::IS_ASCII_DIGIT) {
return;
}
if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_char() {
return;
}
if let Some(radix_val) = constant_full_int(cx, cx.typeck_results(), radix) {
let (num, replacement) = match radix_val {
FullInt::S(10) | FullInt::U(10) => (10, "is_ascii_digit"),
FullInt::S(16) | FullInt::U(16) => (16, "is_ascii_hexdigit"),
_ => return,
};
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
IS_DIGIT_ASCII_RADIX,
expr.span,
&format!("use of `char::is_digit` with literal radix of {}", num),
"try",
format!(
"{}.{}()",
snippet_with_applicability(cx, self_arg.span, "..", &mut applicability),
replacement
),
applicability,
);
}
}

View file

@ -26,6 +26,7 @@ mod implicit_clone;
mod inefficient_to_string;
mod inspect_for_each;
mod into_iter_on_ref;
mod is_digit_ascii_radix;
mod iter_cloned_collect;
mod iter_count;
mod iter_next_slice;
@ -2131,6 +2132,36 @@ declare_clippy_lint! {
"no-op use of `deref` or `deref_mut` method to `Option`."
}
declare_clippy_lint! {
/// ### What it does
/// Finds usages of [`char::is_digit`]
/// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that
/// can be replaced with [`is_ascii_digit`]
/// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or
/// [`is_ascii_hexdigit`]
/// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit).
///
/// ### Why is this bad?
/// `is_digit(..)` is slower and requires specifying the radix.
///
/// ### Example
/// ```rust
/// let c: char = '6';
/// c.is_digit(10);
/// c.is_digit(16);
/// ```
/// Use instead:
/// ```rust
/// let c: char = '6';
/// c.is_ascii_digit();
/// c.is_ascii_hexdigit();
/// ```
#[clippy::version = "1.61.0"]
pub IS_DIGIT_ASCII_RADIX,
style,
"use of `char::is_digit(..)` with literal radix of 10 or 16"
}
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Option<RustcVersion>,
@ -2219,6 +2250,7 @@ impl_lint_pass!(Methods => [
UNNECESSARY_JOIN,
ERR_EXPECT,
NEEDLESS_OPTION_AS_DEREF,
IS_DIGIT_ASCII_RADIX,
]);
/// Extracts a method call name, args, and `Span` of the method name.
@ -2516,6 +2548,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
},
("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
("is_file", []) => filetype_is_file::check(cx, expr, recv),
("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, msrv),
("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
("join", [join_arg]) => {