]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | use crate::utils::{match_def_path, snippet_with_applicability, span_lint_and_sugg}; |
2 | use if_chain::if_chain; | |
3 | use rustc_errors::Applicability; | |
4 | use rustc_hir as hir; | |
5 | use rustc_lint::{LateContext, LateLintPass}; | |
6 | use rustc_middle::ty; | |
7 | use rustc_session::{declare_lint_pass, declare_tool_lint}; | |
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<'tcx> LateLintPass<'tcx> for ToDigitIsSome { | |
35 | fn check_expr(&mut self, cx: &LateContext<'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.typeck_results().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.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 | } |