about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-02-18 19:31:10 +0000
committerbors <bors@rust-lang.org>2022-02-18 19:31:10 +0000
commit73367f8768f7ebffe3bba9c5b94813c2bb5c045f (patch)
treeed76f88e3cd682124bac8fba6897f1173256c7e1
parent02f3c17593e653e30e8835500319ef412855fa34 (diff)
parent88ecdd0804be7c5643eafa4e1960f5e0176903b3 (diff)
downloadrust-73367f8768f7ebffe3bba9c5b94813c2bb5c045f.tar.gz
rust-73367f8768f7ebffe3bba9c5b94813c2bb5c045f.zip
Auto merge of #8381 - Jarcho:cast_possible_truncation_542, r=Manishearth
Lint enum-to-int casts with `cast_possible_truncation`

fixes: #542

~~This will not lint casting a specific variant to an integer. That really should be a new lint as it's definitely a truncation (other than `isize`/`usize` values).~~

changelog: Lint enum-to-int casts with `cast_possible_truncation`
changelog: New lint `cast_enum_truncation`
-rw-r--r--CHANGELOG.md1
-rw-r--r--clippy_lints/src/casts/cast_possible_truncation.rs68
-rw-r--r--clippy_lints/src/casts/mod.rs23
-rw-r--r--clippy_lints/src/casts/utils.rs52
-rw-r--r--clippy_lints/src/lib.register_all.rs1
-rw-r--r--clippy_lints/src/lib.register_lints.rs1
-rw-r--r--clippy_lints/src/lib.register_suspicious.rs1
-rw-r--r--clippy_lints/src/lib.rs1
-rw-r--r--clippy_utils/src/ty.rs59
-rw-r--r--tests/ui/cast.rs136
-rw-r--r--tests/ui/cast.stderr94
11 files changed, 396 insertions, 41 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f41dcce0e00..1b52a6fcd05 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3068,6 +3068,7 @@ Released 2018-09-13
 [`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth
 [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
 [`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons
+[`cast_enum_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_truncation
 [`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless
 [`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation
 [`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap
diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs
index ea74d5acbda..9b189ea1ef8 100644
--- a/clippy_lints/src/casts/cast_possible_truncation.rs
+++ b/clippy_lints/src/casts/cast_possible_truncation.rs
@@ -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) {
@@ -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);
diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs
index 51d47b35454..f2077c569c0 100644
--- a/clippy_lints/src/casts/mod.rs
+++ b/clippy_lints/src/casts/mod.rs
@@ -390,6 +390,25 @@ declare_clippy_lint! {
     "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for casts from an enum type to an integral type which will definitely truncate the
+    /// value.
+    ///
+    /// ### Why is this bad?
+    /// The resulting integral value will not match the value of the variant it came from.
+    ///
+    /// ### Example
+    /// ```rust
+    /// enum E { X = 256 };
+    /// let _ = E::X as u8;
+    /// ```
+    #[clippy::version = "1.60.0"]
+    pub CAST_ENUM_TRUNCATION,
+    suspicious,
+    "casts from an enum type to an integral type which will truncate the value"
+}
+
 pub struct Casts {
     msrv: Option<RustcVersion>,
 }
@@ -415,6 +434,7 @@ impl_lint_pass!(Casts => [
     FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
     CHAR_LIT_AS_U8,
     PTR_AS_PTR,
+    CAST_ENUM_TRUNCATION,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Casts {
@@ -445,13 +465,12 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
             fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
 
             if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
+                cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
                 if cast_from.is_numeric() {
-                    cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
                     cast_possible_wrap::check(cx, expr, cast_from, cast_to);
                     cast_precision_loss::check(cx, expr, cast_from, cast_to);
                     cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
                 }
-
                 cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
             }
         }
diff --git a/clippy_lints/src/casts/utils.rs b/clippy_lints/src/casts/utils.rs
index 00fd0b3473b..bbed766c47a 100644
--- a/clippy_lints/src/casts/utils.rs
+++ b/clippy_lints/src/casts/utils.rs
@@ -1,4 +1,5 @@
-use rustc_middle::ty::{self, IntTy, Ty, TyCtxt, UintTy};
+use clippy_utils::ty::{read_explicit_enum_value, EnumValue};
+use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TyCtxt, UintTy, VariantDiscr};
 
 /// Returns the size in bits of an integral type.
 /// Will return 0 if the type is not an int or uint variant
@@ -23,3 +24,52 @@ pub(super) fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_>) -> u64 {
         _ => 0,
     }
 }
+
+pub(super) fn enum_value_nbits(value: EnumValue) -> u64 {
+    match value {
+        EnumValue::Unsigned(x) => 128 - x.leading_zeros(),
+        EnumValue::Signed(x) if x < 0 => 128 - (-(x + 1)).leading_zeros() + 1,
+        EnumValue::Signed(x) => 128 - x.leading_zeros(),
+    }
+    .into()
+}
+
+pub(super) fn enum_ty_to_nbits(adt: &AdtDef, tcx: TyCtxt<'_>) -> u64 {
+    let mut explicit = 0i128;
+    let (start, end) = adt
+        .variants
+        .iter()
+        .fold((0, i128::MIN), |(start, end), variant| match variant.discr {
+            VariantDiscr::Relative(x) => match explicit.checked_add(i128::from(x)) {
+                Some(x) => (start, end.max(x)),
+                None => (i128::MIN, end),
+            },
+            VariantDiscr::Explicit(id) => match read_explicit_enum_value(tcx, id) {
+                Some(EnumValue::Signed(x)) => {
+                    explicit = x;
+                    (start.min(x), end.max(x))
+                },
+                Some(EnumValue::Unsigned(x)) => match i128::try_from(x) {
+                    Ok(x) => {
+                        explicit = x;
+                        (start, end.max(x))
+                    },
+                    Err(_) => (i128::MIN, end),
+                },
+                None => (start, end),
+            },
+        });
+
+    if start > end {
+        // No variants.
+        0
+    } else {
+        let neg_bits = if start < 0 {
+            128 - (-(start + 1)).leading_zeros() + 1
+        } else {
+            0
+        };
+        let pos_bits = if end > 0 { 128 - end.leading_zeros() } else { 0 };
+        neg_bits.max(pos_bits).into()
+    }
+}
diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs
index a94f6b528b4..c6f8470cd7d 100644
--- a/clippy_lints/src/lib.register_all.rs
+++ b/clippy_lints/src/lib.register_all.rs
@@ -23,6 +23,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
     LintId::of(booleans::LOGIC_BUG),
     LintId::of(booleans::NONMINIMAL_BOOL),
+    LintId::of(casts::CAST_ENUM_TRUNCATION),
     LintId::of(casts::CAST_REF_TO_MUT),
     LintId::of(casts::CHAR_LIT_AS_U8),
     LintId::of(casts::FN_TO_NUMERIC_CAST),
diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs
index df40ca46314..75ef1b0a9d5 100644
--- a/clippy_lints/src/lib.register_lints.rs
+++ b/clippy_lints/src/lib.register_lints.rs
@@ -67,6 +67,7 @@ store.register_lints(&[
     cargo::REDUNDANT_FEATURE_NAMES,
     cargo::WILDCARD_DEPENDENCIES,
     case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
+    casts::CAST_ENUM_TRUNCATION,
     casts::CAST_LOSSLESS,
     casts::CAST_POSSIBLE_TRUNCATION,
     casts::CAST_POSSIBLE_WRAP,
diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs
index da56f800804..6a8859e19d7 100644
--- a/clippy_lints/src/lib.register_suspicious.rs
+++ b/clippy_lints/src/lib.register_suspicious.rs
@@ -7,6 +7,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
     LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
     LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
     LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
+    LintId::of(casts::CAST_ENUM_TRUNCATION),
     LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
     LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
     LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 93b1d178ac2..a21a87899aa 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -25,6 +25,7 @@
 // (Currently there is no way to opt into sysroot crates without `extern crate`.)
 extern crate rustc_ast;
 extern crate rustc_ast_pretty;
+extern crate rustc_attr;
 extern crate rustc_data_structures;
 extern crate rustc_driver;
 extern crate rustc_errors;
diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs
index 958e6d1ec46..7d74b69906d 100644
--- a/clippy_utils/src/ty.rs
+++ b/clippy_utils/src/ty.rs
@@ -10,12 +10,14 @@ use rustc_hir::def_id::DefId;
 use rustc_hir::{Expr, TyKind, Unsafety};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::LateContext;
+use rustc_middle::mir::interpret::{ConstValue, Scalar};
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
 use rustc_middle::ty::{
-    self, AdtDef, Binder, FnSig, IntTy, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy,
+    self, AdtDef, Binder, FnSig, IntTy, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy, VariantDiscr,
 };
 use rustc_span::symbol::Ident;
 use rustc_span::{sym, Span, Symbol, DUMMY_SP};
+use rustc_target::abi::{Size, VariantIdx};
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::query::normalize::AtExt;
 use std::iter;
@@ -515,3 +517,58 @@ pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnS
         }
     }
 }
+
+#[derive(Clone, Copy)]
+pub enum EnumValue {
+    Unsigned(u128),
+    Signed(i128),
+}
+impl core::ops::Add<u32> for EnumValue {
+    type Output = Self;
+    fn add(self, n: u32) -> Self::Output {
+        match self {
+            Self::Unsigned(x) => Self::Unsigned(x + u128::from(n)),
+            Self::Signed(x) => Self::Signed(x + i128::from(n)),
+        }
+    }
+}
+
+/// Attempts to read the given constant as though it were an an enum value.
+#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
+pub fn read_explicit_enum_value(tcx: TyCtxt<'_>, id: DefId) -> Option<EnumValue> {
+    if let Ok(ConstValue::Scalar(Scalar::Int(value))) = tcx.const_eval_poly(id) {
+        match tcx.type_of(id).kind() {
+            ty::Int(_) => Some(EnumValue::Signed(match value.size().bytes() {
+                1 => i128::from(value.assert_bits(Size::from_bytes(1)) as u8 as i8),
+                2 => i128::from(value.assert_bits(Size::from_bytes(2)) as u16 as i16),
+                4 => i128::from(value.assert_bits(Size::from_bytes(4)) as u32 as i32),
+                8 => i128::from(value.assert_bits(Size::from_bytes(8)) as u64 as i64),
+                16 => value.assert_bits(Size::from_bytes(16)) as i128,
+                _ => return None,
+            })),
+            ty::Uint(_) => Some(EnumValue::Unsigned(match value.size().bytes() {
+                1 => value.assert_bits(Size::from_bytes(1)),
+                2 => value.assert_bits(Size::from_bytes(2)),
+                4 => value.assert_bits(Size::from_bytes(4)),
+                8 => value.assert_bits(Size::from_bytes(8)),
+                16 => value.assert_bits(Size::from_bytes(16)),
+                _ => return None,
+            })),
+            _ => None,
+        }
+    } else {
+        None
+    }
+}
+
+/// Gets the value of the given variant.
+pub fn get_discriminant_value(tcx: TyCtxt<'_>, adt: &'_ AdtDef, i: VariantIdx) -> EnumValue {
+    let variant = &adt.variants[i];
+    match variant.discr {
+        VariantDiscr::Explicit(id) => read_explicit_enum_value(tcx, id).unwrap(),
+        VariantDiscr::Relative(x) => match adt.variants[(i.as_usize() - x as usize).into()].discr {
+            VariantDiscr::Explicit(id) => read_explicit_enum_value(tcx, id).unwrap() + x,
+            VariantDiscr::Relative(_) => EnumValue::Unsigned(x.into()),
+        },
+    }
+}
diff --git a/tests/ui/cast.rs b/tests/ui/cast.rs
index ebc1ed5587f..2e31ad3172e 100644
--- a/tests/ui/cast.rs
+++ b/tests/ui/cast.rs
@@ -1,3 +1,6 @@
+#![feature(repr128)]
+#![allow(incomplete_features)]
+
 #[warn(
     clippy::cast_precision_loss,
     clippy::cast_possible_truncation,
@@ -115,4 +118,137 @@ fn main() {
     }) as u8;
     999999u64.clamp(0, 255) as u8;
     999999u64.clamp(0, 256) as u8; // should still be linted
+
+    #[derive(Clone, Copy)]
+    enum E1 {
+        A,
+        B,
+        C,
+    }
+    impl E1 {
+        fn test(self) {
+            let _ = self as u8; // Don't lint. `0..=2` fits in u8
+        }
+    }
+
+    #[derive(Clone, Copy)]
+    enum E2 {
+        A = 255,
+        B,
+    }
+    impl E2 {
+        fn test(self) {
+            let _ = self as u8;
+            let _ = Self::B as u8;
+            let _ = self as i16; // Don't lint. `255..=256` fits in i16
+            let _ = Self::A as u8; // Don't lint.
+        }
+    }
+
+    #[derive(Clone, Copy)]
+    enum E3 {
+        A = -1,
+        B,
+        C = 50,
+    }
+    impl E3 {
+        fn test(self) {
+            let _ = self as i8; // Don't lint. `-1..=50` fits in i8
+        }
+    }
+
+    #[derive(Clone, Copy)]
+    enum E4 {
+        A = -128,
+        B,
+    }
+    impl E4 {
+        fn test(self) {
+            let _ = self as i8; // Don't lint. `-128..=-127` fits in i8
+        }
+    }
+
+    #[derive(Clone, Copy)]
+    enum E5 {
+        A = -129,
+        B = 127,
+    }
+    impl E5 {
+        fn test(self) {
+            let _ = self as i8;
+            let _ = Self::A as i8;
+            let _ = self as i16; // Don't lint. `-129..=127` fits in i16
+            let _ = Self::B as u8; // Don't lint.
+        }
+    }
+
+    #[derive(Clone, Copy)]
+    #[repr(u32)]
+    enum E6 {
+        A = u16::MAX as u32,
+        B,
+    }
+    impl E6 {
+        fn test(self) {
+            let _ = self as i16;
+            let _ = Self::A as u16; // Don't lint. `2^16-1` fits in u16
+            let _ = self as u32; // Don't lint. `2^16-1..=2^16` fits in u32
+            let _ = Self::A as u16; // Don't lint.
+        }
+    }
+
+    #[derive(Clone, Copy)]
+    #[repr(u64)]
+    enum E7 {
+        A = u32::MAX as u64,
+        B,
+    }
+    impl E7 {
+        fn test(self) {
+            let _ = self as usize;
+            let _ = Self::A as usize; // Don't lint.
+            let _ = self as u64; // Don't lint. `2^32-1..=2^32` fits in u64
+        }
+    }
+
+    #[derive(Clone, Copy)]
+    #[repr(i128)]
+    enum E8 {
+        A = i128::MIN,
+        B,
+        C = 0,
+        D = i128::MAX,
+    }
+    impl E8 {
+        fn test(self) {
+            let _ = self as i128; // Don't lint. `-(2^127)..=2^127-1` fits it i128
+        }
+    }
+
+    #[derive(Clone, Copy)]
+    #[repr(u128)]
+    enum E9 {
+        A,
+        B = u128::MAX,
+    }
+    impl E9 {
+        fn test(self) {
+            let _ = Self::A as u8; // Don't lint.
+            let _ = self as u128; // Don't lint. `0..=2^128-1` fits in u128
+        }
+    }
+
+    #[derive(Clone, Copy)]
+    #[repr(usize)]
+    enum E10 {
+        A,
+        B = u32::MAX as usize,
+    }
+    impl E10 {
+        fn test(self) {
+            let _ = self as u16;
+            let _ = Self::B as u32; // Don't lint.
+            let _ = self as u64; // Don't lint.
+        }
+    }
 }
diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr
index edf8790cf33..7a68c0984f1 100644
--- a/tests/ui/cast.stderr
+++ b/tests/ui/cast.stderr
@@ -1,5 +1,5 @@
 error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
-  --> $DIR/cast.rs:11:5
+  --> $DIR/cast.rs:14:5
    |
 LL |     x0 as f32;
    |     ^^^^^^^^^
@@ -7,37 +7,37 @@ LL |     x0 as f32;
    = note: `-D clippy::cast-precision-loss` implied by `-D warnings`
 
 error: casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
-  --> $DIR/cast.rs:13:5
+  --> $DIR/cast.rs:16:5
    |
 LL |     x1 as f32;
    |     ^^^^^^^^^
 
 error: casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
-  --> $DIR/cast.rs:14:5
+  --> $DIR/cast.rs:17:5
    |
 LL |     x1 as f64;
    |     ^^^^^^^^^
 
 error: casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
-  --> $DIR/cast.rs:16:5
+  --> $DIR/cast.rs:19:5
    |
 LL |     x2 as f32;
    |     ^^^^^^^^^
 
 error: casting `u64` to `f32` causes a loss of precision (`u64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
-  --> $DIR/cast.rs:18:5
+  --> $DIR/cast.rs:21:5
    |
 LL |     x3 as f32;
    |     ^^^^^^^^^
 
 error: casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
-  --> $DIR/cast.rs:19:5
+  --> $DIR/cast.rs:22:5
    |
 LL |     x3 as f64;
    |     ^^^^^^^^^
 
 error: casting `f32` to `i32` may truncate the value
-  --> $DIR/cast.rs:21:5
+  --> $DIR/cast.rs:24:5
    |
 LL |     1f32 as i32;
    |     ^^^^^^^^^^^
@@ -45,13 +45,13 @@ LL |     1f32 as i32;
    = note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
 
 error: casting `f32` to `u32` may truncate the value
-  --> $DIR/cast.rs:22:5
+  --> $DIR/cast.rs:25:5
    |
 LL |     1f32 as u32;
    |     ^^^^^^^^^^^
 
 error: casting `f32` to `u32` may lose the sign of the value
-  --> $DIR/cast.rs:22:5
+  --> $DIR/cast.rs:25:5
    |
 LL |     1f32 as u32;
    |     ^^^^^^^^^^^
@@ -59,43 +59,43 @@ LL |     1f32 as u32;
    = note: `-D clippy::cast-sign-loss` implied by `-D warnings`
 
 error: casting `f64` to `f32` may truncate the value
-  --> $DIR/cast.rs:23:5
+  --> $DIR/cast.rs:26:5
    |
 LL |     1f64 as f32;
    |     ^^^^^^^^^^^
 
 error: casting `i32` to `i8` may truncate the value
-  --> $DIR/cast.rs:24:5
+  --> $DIR/cast.rs:27:5
    |
 LL |     1i32 as i8;
    |     ^^^^^^^^^^
 
 error: casting `i32` to `u8` may truncate the value
-  --> $DIR/cast.rs:25:5
+  --> $DIR/cast.rs:28:5
    |
 LL |     1i32 as u8;
    |     ^^^^^^^^^^
 
 error: casting `f64` to `isize` may truncate the value
-  --> $DIR/cast.rs:26:5
+  --> $DIR/cast.rs:29:5
    |
 LL |     1f64 as isize;
    |     ^^^^^^^^^^^^^
 
 error: casting `f64` to `usize` may truncate the value
-  --> $DIR/cast.rs:27:5
+  --> $DIR/cast.rs:30:5
    |
 LL |     1f64 as usize;
    |     ^^^^^^^^^^^^^
 
 error: casting `f64` to `usize` may lose the sign of the value
-  --> $DIR/cast.rs:27:5
+  --> $DIR/cast.rs:30:5
    |
 LL |     1f64 as usize;
    |     ^^^^^^^^^^^^^
 
 error: casting `u8` to `i8` may wrap around the value
-  --> $DIR/cast.rs:29:5
+  --> $DIR/cast.rs:32:5
    |
 LL |     1u8 as i8;
    |     ^^^^^^^^^
@@ -103,52 +103,96 @@ LL |     1u8 as i8;
    = note: `-D clippy::cast-possible-wrap` implied by `-D warnings`
 
 error: casting `u16` to `i16` may wrap around the value
-  --> $DIR/cast.rs:30:5
+  --> $DIR/cast.rs:33:5
    |
 LL |     1u16 as i16;
    |     ^^^^^^^^^^^
 
 error: casting `u32` to `i32` may wrap around the value
-  --> $DIR/cast.rs:31:5
+  --> $DIR/cast.rs:34:5
    |
 LL |     1u32 as i32;
    |     ^^^^^^^^^^^
 
 error: casting `u64` to `i64` may wrap around the value
-  --> $DIR/cast.rs:32:5
+  --> $DIR/cast.rs:35:5
    |
 LL |     1u64 as i64;
    |     ^^^^^^^^^^^
 
 error: casting `usize` to `isize` may wrap around the value
-  --> $DIR/cast.rs:33:5
+  --> $DIR/cast.rs:36:5
    |
 LL |     1usize as isize;
    |     ^^^^^^^^^^^^^^^
 
 error: casting `i32` to `u32` may lose the sign of the value
-  --> $DIR/cast.rs:36:5
+  --> $DIR/cast.rs:39:5
    |
 LL |     -1i32 as u32;
    |     ^^^^^^^^^^^^
 
 error: casting `isize` to `usize` may lose the sign of the value
-  --> $DIR/cast.rs:38:5
+  --> $DIR/cast.rs:41:5
    |
 LL |     -1isize as usize;
    |     ^^^^^^^^^^^^^^^^
 
 error: casting `i64` to `i8` may truncate the value
-  --> $DIR/cast.rs:105:5
+  --> $DIR/cast.rs:108:5
    |
 LL |     (-99999999999i64).min(1) as i8; // should be linted because signed
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: casting `u64` to `u8` may truncate the value
-  --> $DIR/cast.rs:117:5
+  --> $DIR/cast.rs:120:5
    |
 LL |     999999u64.clamp(0, 256) as u8; // should still be linted
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 24 previous errors
+error: casting `main::E2` to `u8` may truncate the value
+  --> $DIR/cast.rs:141:21
+   |
+LL |             let _ = self as u8;
+   |                     ^^^^^^^^^^
+
+error: casting `main::E2::B` to `u8` will truncate the value
+  --> $DIR/cast.rs:142:21
+   |
+LL |             let _ = Self::B as u8;
+   |                     ^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::cast-enum-truncation` implied by `-D warnings`
+
+error: casting `main::E5` to `i8` may truncate the value
+  --> $DIR/cast.rs:178:21
+   |
+LL |             let _ = self as i8;
+   |                     ^^^^^^^^^^
+
+error: casting `main::E5::A` to `i8` will truncate the value
+  --> $DIR/cast.rs:179:21
+   |
+LL |             let _ = Self::A as i8;
+   |                     ^^^^^^^^^^^^^
+
+error: casting `main::E6` to `i16` may truncate the value
+  --> $DIR/cast.rs:193:21
+   |
+LL |             let _ = self as i16;
+   |                     ^^^^^^^^^^^
+
+error: casting `main::E7` to `usize` may truncate the value on targets with 32-bit wide pointers
+  --> $DIR/cast.rs:208:21
+   |
+LL |             let _ = self as usize;
+   |                     ^^^^^^^^^^^^^
+
+error: casting `main::E10` to `u16` may truncate the value
+  --> $DIR/cast.rs:249:21
+   |
+LL |             let _ = self as u16;
+   |                     ^^^^^^^^^^^
+
+error: aborting due to 31 previous errors