]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/from_str_radix_10.rs
Rollup merge of #85760 - ChrisDenton:path-doc-platform-specific, r=m-ou-se
[rust.git] / src / tools / clippy / clippy_lints / src / from_str_radix_10.rs
1 use clippy_utils::diagnostics::span_lint_and_sugg;
2 use clippy_utils::sugg::Sugg;
3 use clippy_utils::ty::is_type_diagnostic_item;
4 use if_chain::if_chain;
5 use rustc_errors::Applicability;
6 use rustc_hir::{def, Expr, ExprKind, PrimTy, QPath, TyKind};
7 use rustc_lint::{LateContext, LateLintPass};
8 use rustc_middle::ty::Ty;
9 use rustc_session::{declare_lint_pass, declare_tool_lint};
10 use rustc_span::symbol::sym;
11
12 declare_clippy_lint! {
13     /// **What it does:**
14     /// Checks for function invocations of the form `primitive::from_str_radix(s, 10)`
15     ///
16     /// **Why is this bad?**
17     /// This specific common use case can be rewritten as `s.parse::<primitive>()`
18     /// (and in most cases, the turbofish can be removed), which reduces code length
19     /// and complexity.
20     ///
21     /// **Known problems:**
22     /// This lint may suggest using (&<expression>).parse() instead of <expression>.parse() directly
23     /// in some cases, which is correct but adds unnecessary complexity to the code.
24     ///
25     /// **Example:**
26     ///
27     /// ```ignore
28     /// let input: &str = get_input();
29     /// let num = u16::from_str_radix(input, 10)?;
30     /// ```
31     /// Use instead:
32     /// ```ignore
33     /// let input: &str = get_input();
34     /// let num: u16 = input.parse()?;
35     /// ```
36     pub FROM_STR_RADIX_10,
37     style,
38     "from_str_radix with radix 10"
39 }
40
41 declare_lint_pass!(FromStrRadix10 => [FROM_STR_RADIX_10]);
42
43 impl LateLintPass<'tcx> for FromStrRadix10 {
44     fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) {
45         if_chain! {
46             if let ExprKind::Call(maybe_path, arguments) = &exp.kind;
47             if let ExprKind::Path(QPath::TypeRelative(ty, pathseg)) = &maybe_path.kind;
48
49             // check if the first part of the path is some integer primitive
50             if let TyKind::Path(ty_qpath) = &ty.kind;
51             let ty_res = cx.qpath_res(ty_qpath, ty.hir_id);
52             if let def::Res::PrimTy(prim_ty) = ty_res;
53             if matches!(prim_ty, PrimTy::Int(_) | PrimTy::Uint(_));
54
55             // check if the second part of the path indeed calls the associated
56             // function `from_str_radix`
57             if pathseg.ident.name.as_str() == "from_str_radix";
58
59             // check if the second argument is a primitive `10`
60             if arguments.len() == 2;
61             if let ExprKind::Lit(lit) = &arguments[1].kind;
62             if let rustc_ast::ast::LitKind::Int(10, _) = lit.node;
63
64             then {
65                 let expr = if let ExprKind::AddrOf(_, _, expr) = &arguments[0].kind {
66                     let ty = cx.typeck_results().expr_ty(expr);
67                     if is_ty_stringish(cx, ty) {
68                         expr
69                     } else {
70                         &arguments[0]
71                     }
72                 } else {
73                     &arguments[0]
74                 };
75
76                 let sugg = Sugg::hir_with_applicability(
77                     cx,
78                     expr,
79                     "<string>",
80                     &mut Applicability::MachineApplicable
81                 ).maybe_par();
82
83                 span_lint_and_sugg(
84                     cx,
85                     FROM_STR_RADIX_10,
86                     exp.span,
87                     "this call to `from_str_radix` can be replaced with a call to `str::parse`",
88                     "try",
89                     format!("{}.parse::<{}>()", sugg, prim_ty.name_str()),
90                     Applicability::MaybeIncorrect
91                 );
92             }
93         }
94     }
95 }
96
97 /// Checks if a Ty is `String` or `&str`
98 fn is_ty_stringish(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
99     is_type_diagnostic_item(cx, ty, sym::string_type) || is_type_diagnostic_item(cx, ty, sym::str)
100 }