]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
Rollup merge of #88215 - jyn514:lazy-loading, r=petrochenkov
[rust.git] / src / tools / clippy / clippy_lints / src / casts / cast_lossless.rs
1 use clippy_utils::diagnostics::span_lint_and_sugg;
2 use clippy_utils::in_constant;
3 use clippy_utils::source::snippet_opt;
4 use clippy_utils::ty::is_isize_or_usize;
5 use rustc_errors::Applicability;
6 use rustc_hir::{Expr, ExprKind};
7 use rustc_lint::LateContext;
8 use rustc_middle::ty::{self, FloatTy, Ty};
9
10 use super::{utils, CAST_LOSSLESS};
11
12 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
13     if !should_lint(cx, expr, cast_from, cast_to) {
14         return;
15     }
16
17     // The suggestion is to use a function call, so if the original expression
18     // has parens on the outside, they are no longer needed.
19     let mut applicability = Applicability::MachineApplicable;
20     let opt = snippet_opt(cx, cast_op.span);
21     let sugg = opt.as_ref().map_or_else(
22         || {
23             applicability = Applicability::HasPlaceholders;
24             ".."
25         },
26         |snip| {
27             if should_strip_parens(cast_op, snip) {
28                 &snip[1..snip.len() - 1]
29             } else {
30                 snip.as_str()
31             }
32         },
33     );
34
35     span_lint_and_sugg(
36         cx,
37         CAST_LOSSLESS,
38         expr.span,
39         &format!(
40             "casting `{}` to `{}` may become silently lossy if you later change the type",
41             cast_from, cast_to
42         ),
43         "try",
44         format!("{}::from({})", cast_to, sugg),
45         applicability,
46     );
47 }
48
49 fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) -> bool {
50     // Do not suggest using From in consts/statics until it is valid to do so (see #2267).
51     if in_constant(cx, expr.hir_id) {
52         return false;
53     }
54
55     match (cast_from.is_integral(), cast_to.is_integral()) {
56         (true, true) => {
57             let cast_signed_to_unsigned = cast_from.is_signed() && !cast_to.is_signed();
58             let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
59             let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
60             !is_isize_or_usize(cast_from)
61                 && !is_isize_or_usize(cast_to)
62                 && from_nbits < to_nbits
63                 && !cast_signed_to_unsigned
64         },
65
66         (true, false) => {
67             let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
68             let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() {
69                 32
70             } else {
71                 64
72             };
73             from_nbits < to_nbits
74         },
75
76         (_, _) => {
77             matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64))
78         },
79     }
80 }
81
82 fn should_strip_parens(cast_expr: &Expr<'_>, snip: &str) -> bool {
83     if let ExprKind::Binary(_, _, _) = cast_expr.kind {
84         if snip.starts_with('(') && snip.ends_with(')') {
85             return true;
86         }
87     }
88     false
89 }