blob: 707fc2a8eed358507b658bd2740084f8c5a49591 [file] [log] [blame] [edit]
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,
}
}