]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/casts/cast_possible_truncation.rs
Auto merge of #8670 - yoav-lavi:main, r=giraffate
[rust.git] / clippy_lints / src / casts / cast_possible_truncation.rs
index 2384e4a37483fd2f88606c1a6f007322e88b00e2..64f87c80f8d147a5e3e21aed776582434d465fee 100644 (file)
@@ -1,13 +1,15 @@
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::expr_or_init;
-use clippy_utils::ty::is_isize_or_usize;
+use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize};
+use rustc_ast::ast;
+use rustc_attr::IntType;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, FloatTy, Ty};
 
-use super::{utils, CAST_POSSIBLE_TRUNCATION};
+use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION};
 
 fn constant_int(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
     if let Some((Constant::Int(c), _)) = constant(cx, cx.typeck_results(), expr) {
@@ -27,21 +29,19 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b
         ExprKind::Block(block, _) => block.expr.map_or(nbits, |e| apply_reductions(cx, nbits, e, signed)),
         ExprKind::Binary(op, left, right) => match op.node {
             BinOpKind::Div => {
-                apply_reductions(cx, nbits, left, signed)
-                    - (if signed {
-                        0 // let's be conservative here
-                    } else {
-                        // by dividing by 1, we remove 0 bits, etc.
-                        get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1))
-                    })
+                apply_reductions(cx, nbits, left, signed).saturating_sub(if signed {
+                    // let's be conservative here
+                    0
+                } else {
+                    // by dividing by 1, we remove 0 bits, etc.
+                    get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1))
+                })
             },
             BinOpKind::Rem | BinOpKind::BitAnd => get_constant_bits(cx, right)
                 .unwrap_or(u64::max_value())
                 .min(apply_reductions(cx, nbits, left, signed)),
-            BinOpKind::Shr => {
-                apply_reductions(cx, nbits, left, signed)
-                    - constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))
-            },
+            BinOpKind::Shr => apply_reductions(cx, nbits, left, signed)
+                .saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))),
             _ => nbits,
         },
         ExprKind::MethodCall(method, [left, right], _) => {
@@ -110,27 +110,44 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
         },
 
         (ty::Adt(def, _), true) if def.is_enum() => {
-            if let ExprKind::Path(p) = &cast_expr.kind
-                && let Res::Def(DefKind::Ctor(..), _) = cx.qpath_res(p, cast_expr.hir_id)
+            let (from_nbits, variant) = if let ExprKind::Path(p) = &cast_expr.kind
+                && let Res::Def(DefKind::Ctor(..), id) = cx.qpath_res(p, cast_expr.hir_id)
             {
-                return
-            }
-
-            let from_nbits = utils::enum_ty_to_nbits(def, cx.tcx);
+                let i = def.variant_index_with_ctor_id(id);
+                let variant = def.variant(i);
+                let nbits = utils::enum_value_nbits(get_discriminant_value(cx.tcx, *def, i));
+                (nbits, Some(variant))
+            } else {
+                (utils::enum_ty_to_nbits(*def, cx.tcx), None)
+            };
             let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
 
-            let suffix = if is_isize_or_usize(cast_to) {
-                if from_nbits > 32 {
-                    " on targets with 32-bit wide pointers"
-                } else {
-                    return;
-                }
-            } else if to_nbits < from_nbits {
-                ""
-            } else {
-                return;
+            let cast_from_ptr_size = def.repr().int.map_or(true, |ty| {
+                matches!(
+                    ty,
+                    IntType::SignedInt(ast::IntTy::Isize) | IntType::UnsignedInt(ast::UintTy::Usize)
+                )
+            });
+            let suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) {
+                (false, false) if from_nbits > to_nbits => "",
+                (true, false) if from_nbits > to_nbits => "",
+                (false, true) if from_nbits > 64 => "",
+                (false, true) if from_nbits > 32 => " on targets with 32-bit wide pointers",
+                _ => return,
             };
 
+            if let Some(variant) = variant {
+                span_lint(
+                    cx,
+                    CAST_ENUM_TRUNCATION,
+                    expr.span,
+                    &format!(
+                        "casting `{}::{}` to `{}` will truncate the value{}",
+                        cast_from, variant.name, cast_to, suffix,
+                    ),
+                );
+                return;
+            }
             format!(
                 "casting `{}` to `{}` may truncate the value{}",
                 cast_from, cast_to, suffix,