]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs
Merge commit '7248d06384c6a90de58c04c1f46be88821278d8b' into sync-from-clippy
[rust.git] / src / tools / clippy / clippy_lints / src / casts / cast_sign_loss.rs
1 use clippy_utils::consts::{constant, Constant};
2 use clippy_utils::diagnostics::span_lint;
3 use clippy_utils::{method_chain_args, sext};
4 use if_chain::if_chain;
5 use rustc_hir::{Expr, ExprKind};
6 use rustc_lint::LateContext;
7 use rustc_middle::ty::{self, Ty};
8
9 use super::CAST_SIGN_LOSS;
10
11 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
12     if should_lint(cx, cast_op, cast_from, cast_to) {
13         span_lint(
14             cx,
15             CAST_SIGN_LOSS,
16             expr.span,
17             &format!(
18                 "casting `{}` to `{}` may lose the sign of the value",
19                 cast_from, cast_to
20             ),
21         );
22     }
23 }
24
25 fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) -> bool {
26     match (cast_from.is_integral(), cast_to.is_integral()) {
27         (true, true) => {
28             if !cast_from.is_signed() || cast_to.is_signed() {
29                 return false;
30             }
31
32             // Don't lint for positive constants.
33             let const_val = constant(cx, cx.typeck_results(), cast_op);
34             if_chain! {
35                 if let Some((Constant::Int(n), _)) = const_val;
36                 if let ty::Int(ity) = *cast_from.kind();
37                 if sext(cx.tcx, n, ity) >= 0;
38                 then {
39                     return false;
40                 }
41             }
42
43             // Don't lint for the result of methods that always return non-negative values.
44             if let ExprKind::MethodCall(path, ..) = cast_op.kind {
45                 let mut method_name = path.ident.name.as_str();
46                 let allowed_methods = ["abs", "checked_abs", "rem_euclid", "checked_rem_euclid"];
47
48                 if_chain! {
49                     if method_name == "unwrap";
50                     if let Some(arglist) = method_chain_args(cast_op, &["unwrap"]);
51                     if let ExprKind::MethodCall(inner_path, ..) = &arglist[0].0.kind;
52                     then {
53                         method_name = inner_path.ident.name.as_str();
54                     }
55                 }
56
57                 if allowed_methods.iter().any(|&name| method_name == name) {
58                     return false;
59                 }
60             }
61
62             true
63         },
64
65         (false, true) => !cast_to.is_signed(),
66
67         (_, _) => false,
68     }
69 }