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) {
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], _) => {
},
(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,