Use None::<u64> instead of 0 to denote an absence of value (#14957)

`clippy_lints::casts::utils::int_ty_to_nbits` used to return `0` to
indicate that the argument was not an integral type. It is more
idiomatic to use `Option<u64>` as a return type.

Also, I've added a few `FIXME:` at places where only `f32` and `f64` are
considered, as we now have `f16` and `f128` to deal with.

changelog: none
This commit is contained in:
llogiq 2025-06-02 12:41:09 +00:00 committed by GitHub
commit 6fa2866a96
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 45 additions and 65 deletions

View file

@ -76,19 +76,20 @@ fn should_lint(cx: &LateContext<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: M
return false;
}
match (cast_from.is_integral(), cast_to.is_integral()) {
(true, true) => {
match (
utils::int_ty_to_nbits(cx.tcx, cast_from),
utils::int_ty_to_nbits(cx.tcx, cast_to),
) {
(Some(from_nbits), Some(to_nbits)) => {
let cast_signed_to_unsigned = cast_from.is_signed() && !cast_to.is_signed();
let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
!is_isize_or_usize(cast_from)
&& !is_isize_or_usize(cast_to)
&& from_nbits < to_nbits
&& !cast_signed_to_unsigned
},
(true, false) => {
let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
(Some(from_nbits), None) => {
// FIXME: handle `f16` and `f128`
let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() {
32
} else {
@ -96,9 +97,7 @@ fn should_lint(cx: &LateContext<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: M
};
!is_isize_or_usize(cast_from) && from_nbits < to_nbits
},
(false, true) if matches!(cast_from.kind(), ty::Bool) && msrv.meets(cx, msrvs::FROM_BOOL) => true,
(_, _) => {
matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64))
},
(None, Some(_)) if cast_from.is_bool() && msrv.meets(cx, msrvs::FROM_BOOL) => true,
_ => matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64)),
}
}

View file

@ -91,15 +91,14 @@ pub(super) fn check(
cast_to: Ty<'_>,
cast_to_span: Span,
) {
let msg = match (cast_from.kind(), cast_to.is_integral()) {
(ty::Int(_) | ty::Uint(_), true) => {
let msg = match (cast_from.kind(), utils::int_ty_to_nbits(cx.tcx, cast_to)) {
(ty::Int(_) | ty::Uint(_), Some(to_nbits)) => {
let from_nbits = apply_reductions(
cx,
utils::int_ty_to_nbits(cast_from, cx.tcx),
utils::int_ty_to_nbits(cx.tcx, cast_from).unwrap(),
cast_expr,
cast_from.is_signed(),
);
let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
let (should_lint, suffix) = match (is_isize_or_usize(cast_from), is_isize_or_usize(cast_to)) {
(true, true) | (false, false) => (to_nbits < from_nbits, ""),
@ -121,7 +120,7 @@ pub(super) fn check(
format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",)
},
(ty::Adt(def, _), true) if def.is_enum() => {
(ty::Adt(def, _), Some(to_nbits)) 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)
{
@ -132,7 +131,6 @@ pub(super) fn check(
} 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.is_none_or(|ty| matches!(ty, IntegerType::Pointer(_),));
let suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) {
@ -157,11 +155,11 @@ pub(super) fn check(
format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}")
},
(ty::Float(_), true) => {
(ty::Float(_), Some(_)) => {
format!("casting `{cast_from}` to `{cast_to}` may truncate the value")
},
(ty::Float(FloatTy::F64), false) if matches!(cast_to.kind(), &ty::Float(FloatTy::F32)) => {
(ty::Float(FloatTy::F64), None) if matches!(cast_to.kind(), &ty::Float(FloatTy::F32)) => {
"casting `f64` to `f32` may truncate the value".to_string()
},

View file

@ -17,9 +17,12 @@ enum EmitState {
}
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
if !(cast_from.is_integral() && cast_to.is_integral()) {
let (Some(from_nbits), Some(to_nbits)) = (
utils::int_ty_to_nbits(cx.tcx, cast_from),
utils::int_ty_to_nbits(cx.tcx, cast_to),
) else {
return;
}
};
// emit a lint if a cast is:
// 1. unsigned to signed
@ -35,9 +38,6 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca
return;
}
let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
let should_lint = match (cast_from.is_ptr_sized_integral(), cast_to.is_ptr_sized_integral()) {
(true, true) => {
// casts between two ptr sized integers are trivially always the same size

View file

@ -7,15 +7,14 @@ use rustc_middle::ty::{self, FloatTy, Ty};
use super::{CAST_PRECISION_LOSS, utils};
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
if !cast_from.is_integral() || cast_to.is_integral() {
let Some(from_nbits) = utils::int_ty_to_nbits(cx.tcx, cast_from) else {
return;
}
};
let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
let to_nbits = if cast_to.kind() == &ty::Float(FloatTy::F32) {
32
} else {
64
// FIXME: handle `f16` and `f128`
let to_nbits = match cast_to.kind() {
ty::Float(f @ (FloatTy::F32 | FloatTy::F64)) => f.bit_width(),
_ => return,
};
if !(is_isize_or_usize(cast_from) || from_nbits >= to_nbits) {
@ -29,9 +28,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca
let from_nbits_str = if arch_dependent {
"64".to_owned()
} else if is_isize_or_usize(cast_from) {
// FIXME: handle 16 bits `usize` type
"32 or 64".to_owned()
} else {
utils::int_ty_to_nbits(cast_from, cx.tcx).to_string()
from_nbits.to_string()
};
span_lint(

View file

@ -3,24 +3,22 @@ use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty, UintTy};
use rustc_middle::ty::{self, Ty};
use super::{FN_TO_NUMERIC_CAST, utils};
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
// We only want to check casts to `ty::Uint` or `ty::Int`
match cast_to.kind() {
ty::Uint(_) | ty::Int(..) => { /* continue on */ },
_ => return,
}
let Some(to_nbits) = utils::int_ty_to_nbits(cx.tcx, cast_to) else {
return;
};
match cast_from.kind() {
ty::FnDef(..) | ty::FnPtr(..) => {
let mut applicability = Applicability::MaybeIncorrect;
let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability);
let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
if (to_nbits >= cx.tcx.data_layout.pointer_size.bits()) && (*cast_to.kind() != ty::Uint(UintTy::Usize)) {
if to_nbits >= cx.tcx.data_layout.pointer_size.bits() && !cast_to.is_usize() {
let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability);
span_lint_and_sugg(
cx,
FN_TO_NUMERIC_CAST,

View file

@ -9,16 +9,14 @@ use super::{FN_TO_NUMERIC_CAST_WITH_TRUNCATION, utils};
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
// We only want to check casts to `ty::Uint` or `ty::Int`
match cast_to.kind() {
ty::Uint(_) | ty::Int(..) => { /* continue on */ },
_ => return,
}
let Some(to_nbits) = utils::int_ty_to_nbits(cx.tcx, cast_to) else {
return;
};
match cast_from.kind() {
ty::FnDef(..) | ty::FnPtr(..) => {
let mut applicability = Applicability::MaybeIncorrect;
let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability);
let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
if to_nbits < cx.tcx.data_layout.pointer_size.bits() {
span_lint_and_sugg(
cx,

View file

@ -1,27 +1,14 @@
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.
/// Will return 0 if the type is not an int or uint variant
pub(super) fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_>) -> u64 {
match typ.kind() {
ty::Int(i) => match i {
IntTy::Isize => tcx.data_layout.pointer_size.bits(),
IntTy::I8 => 8,
IntTy::I16 => 16,
IntTy::I32 => 32,
IntTy::I64 => 64,
IntTy::I128 => 128,
},
ty::Uint(i) => match i {
UintTy::Usize => tcx.data_layout.pointer_size.bits(),
UintTy::U8 => 8,
UintTy::U16 => 16,
UintTy::U32 => 32,
UintTy::U64 => 64,
UintTy::U128 => 128,
},
_ => 0,
/// 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,
}
}