]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/casts/cast_possible_truncation.rs
Extract some util functions
[rust.git] / clippy_lints / src / casts / cast_possible_truncation.rs
index 4af412ccaf35d7d0946429c5b997f9ae7081792e..9b189ea1ef8fb102396b7cefa76829bcff426ee5 100644 (file)
@@ -1,12 +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) {
@@ -43,7 +46,7 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b
             },
             _ => nbits,
         },
-        ExprKind::MethodCall(method, _, [left, right], _) => {
+        ExprKind::MethodCall(method, [left, right], _) => {
             if signed {
                 return nbits;
             }
@@ -54,7 +57,7 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b
             };
             apply_reductions(cx, nbits, left, signed).min(max_bits.unwrap_or(u64::max_value()))
         },
-        ExprKind::MethodCall(method, _, [_, lo, hi], _) => {
+        ExprKind::MethodCall(method, [_, lo, hi], _) => {
             if method.ident.as_str() == "clamp" {
                 //FIXME: make this a diagnostic item
                 if let (Some(lo_bits), Some(hi_bits)) = (get_constant_bits(cx, lo), get_constant_bits(cx, hi)) {
@@ -63,7 +66,7 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b
             }
             nbits
         },
-        ExprKind::MethodCall(method, _, [_value], _) => {
+        ExprKind::MethodCall(method, [_value], _) => {
             if method.ident.name.as_str() == "signum" {
                 0 // do not lint if cast comes from a `signum` function
             } else {
@@ -75,8 +78,8 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b
 }
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
-    let msg = match (cast_from.is_integral(), cast_to.is_integral()) {
-        (true, true) => {
+    let msg = match (cast_from.kind(), cast_to.is_integral()) {
+        (ty::Int(_) | ty::Uint(_), true) => {
             let from_nbits = apply_reductions(
                 cx,
                 utils::int_ty_to_nbits(cast_from, cx.tcx),
@@ -108,19 +111,60 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
             )
         },
 
-        (false, true) => {
-            format!("casting `{}` to `{}` may truncate the value", cast_from, cast_to)
-        },
-
-        (_, _) => {
-            if matches!(cast_from.kind(), &ty::Float(FloatTy::F64))
-                && matches!(cast_to.kind(), &ty::Float(FloatTy::F32))
+        (ty::Adt(def, _), true) if def.is_enum() => {
+            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)
             {
-                "casting `f64` to `f32` may truncate the value".to_string()
+                let i = def.variant_index_with_ctor_id(id);
+                let variant = &def.variants[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 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,
+            )
         },
+
+        (ty::Float(_), true) => {
+            format!("casting `{}` to `{}` may truncate the value", cast_from, cast_to)
+        },
+
+        (ty::Float(FloatTy::F64), false) if matches!(cast_to.kind(), &ty::Float(FloatTy::F32)) => {
+            "casting `f64` to `f32` may truncate the value".to_string()
+        },
+
+        _ => return,
     };
 
     span_lint(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg);