diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 6c938062c197..9b189ea1ef8f 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -1,13 +1,13 @@ 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, VariantDiscr}; +use rustc_middle::ty::{self, FloatTy, Ty}; use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION}; @@ -117,17 +117,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, { let i = def.variant_index_with_ctor_id(id); let variant = &def.variants[i]; - let nbits: u64 = match variant.discr { - VariantDiscr::Explicit(id) => utils::read_explicit_enum_value(cx.tcx, id).unwrap().nbits(), - VariantDiscr::Relative(x) => { - match def.variants[(i.as_usize() - x as usize).into()].discr { - VariantDiscr::Explicit(id) => { - utils::read_explicit_enum_value(cx.tcx, id).unwrap().add(x).nbits() - } - VariantDiscr::Relative(_) => (32 - x.leading_zeros()).into(), - } - } - }; + 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) diff --git a/clippy_lints/src/casts/utils.rs b/clippy_lints/src/casts/utils.rs index fab0c194b375..bbed766c47a8 100644 --- a/clippy_lints/src/casts/utils.rs +++ b/clippy_lints/src/casts/utils.rs @@ -1,7 +1,5 @@ -use rustc_middle::mir::interpret::{ConstValue, Scalar}; +use clippy_utils::ty::{read_explicit_enum_value, EnumValue}; use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TyCtxt, UintTy, VariantDiscr}; -use rustc_span::def_id::DefId; -use rustc_target::abi::Size; /// Returns the size in bits of an integral type. /// Will return 0 if the type is not an int or uint variant @@ -27,53 +25,13 @@ pub(super) fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_>) -> u64 { } } -pub(super) enum EnumValue { - Unsigned(u128), - Signed(i128), -} -impl EnumValue { - pub(super) fn add(self, n: u32) -> Self { - match self { - Self::Unsigned(x) => Self::Unsigned(x + u128::from(n)), - Self::Signed(x) => Self::Signed(x + i128::from(n)), - } - } - - pub(super) fn nbits(self) -> u64 { - match self { - Self::Unsigned(x) => 128 - x.leading_zeros(), - Self::Signed(x) if x < 0 => 128 - (-(x + 1)).leading_zeros() + 1, - Self::Signed(x) => 128 - x.leading_zeros(), - } - .into() - } -} - -#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] -pub(super) fn read_explicit_enum_value(tcx: TyCtxt<'_>, id: DefId) -> Option { - 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 +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 { diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 958e6d1ec461..9d13ab9fb33c 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 Self { + 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 { + 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().add(x), + VariantDiscr::Relative(_) => EnumValue::Unsigned(x.into()), + }, + } +}