| use clippy_utils::ty::{EnumValue, read_explicit_enum_value}; |
| use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TyCtxt, UintTy, VariantDiscr}; |
| |
| /// Returns the size in bits of an integral type, or `None` if `ty` is not an |
| /// integral type. |
| pub(super) fn int_ty_to_nbits(tcx: TyCtxt<'_>, ty: Ty<'_>) -> Option<u64> { |
| match ty.kind() { |
| ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(tcx.data_layout.pointer_size().bits()), |
| ty::Int(i) => i.bit_width(), |
| ty::Uint(i) => i.bit_width(), |
| _ => 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 { |
| 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() |
| } |
| } |
| |
| pub(super) enum CastTo { |
| Signed, |
| Unsigned, |
| } |
| /// Returns `Some` if the type cast is between 2 integral types that differ |
| /// only in signedness, otherwise `None`. The value of `Some` is which |
| /// signedness is casted to. |
| pub(super) fn is_signedness_cast(cast_from: Ty<'_>, cast_to: Ty<'_>) -> Option<CastTo> { |
| match (cast_from.kind(), cast_to.kind()) { |
| (ty::Int(from), ty::Uint(to)) if from.to_unsigned() == *to => Some(CastTo::Unsigned), |
| (ty::Uint(from), ty::Int(to)) if *from == to.to_unsigned() => Some(CastTo::Signed), |
| _ => None, |
| } |
| } |