diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index c1d6cec1b62e..b32d541356e3 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -7,7 +7,7 @@ use clippy_utils::ty::is_isize_or_usize; use rustc_errors::Applicability; use rustc_hir::{Expr, QPath, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, FloatTy, Ty}; +use rustc_middle::ty::{self, Ty}; use rustc_span::hygiene; use super::{CAST_LOSSLESS, utils}; @@ -76,28 +76,42 @@ fn should_lint(cx: &LateContext<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: M return false; } - 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(); - !is_isize_or_usize(cast_from) - && !is_isize_or_usize(cast_to) - && from_nbits < to_nbits - && !cast_signed_to_unsigned - }, + if matches!(cast_from.kind(), ty::Bool) && cast_to.is_integral() { + return msrv.meets(cx, msrvs::FROM_BOOL); + } - (Some(from_nbits), None) => { - // FIXME: handle `f16` and `f128` - let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() { - 32 - } else { - 64 - }; - !is_isize_or_usize(cast_from) && from_nbits < to_nbits - }, - (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)), + if cast_from.is_numeric() { + return match (cast_from.is_integral(), cast_to.is_integral()) { + (true, true) => { + 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); + let to_nbits = utils::float_ty_to_nbits(cast_to); + if from_nbits == 64 && to_nbits == 128 { + // FIXME(f16_f128): https://github.com/rust-lang/rust/blob/91fad92585b2dafc52a074e502b2a6c1f093ca35/library/core/src/convert/num.rs#L171 + return false; + } + !is_isize_or_usize(cast_from) && from_nbits < to_nbits + }, + (false, true) => false, + (false, false) => { + let from_nbits = utils::float_ty_to_nbits(cast_from); + let to_nbits = utils::float_ty_to_nbits(cast_to); + if from_nbits == 16 && to_nbits == 32 { + // FIXME(f16_f128): https://github.com/rust-lang/rust/issues/123831 + return false; + } + from_nbits < to_nbits + }, + }; } + + false } diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index a2ecb5fb44ae..8242e8774644 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -9,7 +9,7 @@ use rustc_errors::{Applicability, Diag}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, FloatTy, Ty}; +use rustc_middle::ty::{self, Ty}; use rustc_span::Span; use super::{CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION, utils}; @@ -91,14 +91,15 @@ pub(super) fn check( cast_to: Ty<'_>, cast_to_span: Span, ) { - let msg = match (cast_from.kind(), utils::int_ty_to_nbits(cx.tcx, cast_to)) { - (ty::Int(_) | ty::Uint(_), Some(to_nbits)) => { + let msg = match (cast_from.kind(), cast_to.is_integral()) { + (ty::Int(_) | ty::Uint(_), true) => { let from_nbits = apply_reductions( cx, - utils::int_ty_to_nbits(cx.tcx, cast_from).unwrap(), + utils::int_ty_to_nbits(cast_from, cx.tcx), 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, ""), @@ -120,7 +121,7 @@ pub(super) fn check( format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",) }, - (ty::Adt(def, _), Some(to_nbits)) if def.is_enum() => { + (ty::Adt(def, _), true) 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) { @@ -131,6 +132,7 @@ 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)) { @@ -155,12 +157,18 @@ pub(super) fn check( format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}") }, - (ty::Float(_), Some(_)) => { + (ty::Float(_), true) => { format!("casting `{cast_from}` to `{cast_to}` may truncate the value") }, - (ty::Float(FloatTy::F64), None) if matches!(cast_to.kind(), &ty::Float(FloatTy::F32)) => { - "casting `f64` to `f32` may truncate the value".to_string() + (ty::Float(_), false) => { + let from_nbits = utils::float_ty_to_nbits(cast_from); + let to_nbits = utils::float_ty_to_nbits(cast_to); + if from_nbits > to_nbits { + format!("casting `f{from_nbits}` to `f{to_nbits}` may truncate the value") + } else { + return; + } }, _ => return, diff --git a/clippy_lints/src/casts/cast_possible_wrap.rs b/clippy_lints/src/casts/cast_possible_wrap.rs index e26c03ccda93..504d0a267e47 100644 --- a/clippy_lints/src/casts/cast_possible_wrap.rs +++ b/clippy_lints/src/casts/cast_possible_wrap.rs @@ -17,12 +17,9 @@ enum EmitState { } pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { - 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 { + if !(cast_from.is_integral() && cast_to.is_integral()) { return; - }; + } // emit a lint if a cast is: // 1. unsigned to signed @@ -38,6 +35,9 @@ 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 diff --git a/clippy_lints/src/casts/cast_precision_loss.rs b/clippy_lints/src/casts/cast_precision_loss.rs index 712e38db499f..33afcaf76cbd 100644 --- a/clippy_lints/src/casts/cast_precision_loss.rs +++ b/clippy_lints/src/casts/cast_precision_loss.rs @@ -2,28 +2,25 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_isize_or_usize; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_middle::ty::{self, FloatTy, Ty}; +use rustc_middle::ty::Ty; use super::{CAST_PRECISION_LOSS, utils}; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { - let Some(from_nbits) = utils::int_ty_to_nbits(cx.tcx, cast_from) else { + if !cast_from.is_integral() || cast_to.is_integral() { return; - }; + } - // FIXME: handle `f16` and `f128` - let to_nbits = match cast_to.kind() { - ty::Float(f @ (FloatTy::F32 | FloatTy::F64)) => f.bit_width(), - _ => return, - }; + let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx); + let to_nbits = utils::float_ty_to_nbits(cast_to); if !(is_isize_or_usize(cast_from) || from_nbits >= to_nbits) { return; } - let cast_to_f64 = to_nbits == 64; - let mantissa_nbits = if cast_to_f64 { 52 } else { 23 }; - let arch_dependent = is_isize_or_usize(cast_from) && cast_to_f64; + let mantissa_nbits = utils::float_ty_to_mantissa_nbits(cast_to); + + let arch_dependent = is_isize_or_usize(cast_from) && to_nbits == 64; let arch_dependent_str = "on targets with 64-bit wide pointers "; let from_nbits_str = if arch_dependent { "64".to_owned() @@ -42,7 +39,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca "casting `{0}` to `{1}` causes a loss of precision {2}(`{0}` is {3} bits wide, \ but `{1}`'s mantissa is only {4} bits wide)", cast_from, - if cast_to_f64 { "f64" } else { "f32" }, + format!("f{to_nbits}"), if arch_dependent { arch_dependent_str } else { "" }, from_nbits_str, mantissa_nbits diff --git a/clippy_lints/src/casts/fn_to_numeric_cast.rs b/clippy_lints/src/casts/fn_to_numeric_cast.rs index 105477093b56..066b925d2d66 100644 --- a/clippy_lints/src/casts/fn_to_numeric_cast.rs +++ b/clippy_lints/src/casts/fn_to_numeric_cast.rs @@ -9,16 +9,18 @@ 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` - let Some(to_nbits) = utils::int_ty_to_nbits(cx.tcx, cast_to) else { - return; - }; + match cast_to.kind() { + ty::Uint(_) | ty::Int(..) => { /* continue on */ }, + _ => 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.is_usize() { - let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability); + if (to_nbits >= cx.tcx.data_layout.pointer_size.bits()) && !cast_to.is_usize() { span_lint_and_sugg( cx, FN_TO_NUMERIC_CAST, diff --git a/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs b/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs index 700b7d0d4266..18ea4a346038 100644 --- a/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs +++ b/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs @@ -9,13 +9,16 @@ 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` - let Some(to_nbits) = utils::int_ty_to_nbits(cx.tcx, cast_to) else { - return; - }; + match cast_to.kind() { + ty::Uint(_) | ty::Int(..) => { /* continue on */ }, + _ => 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( diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 8e8c55cf3832..79f7e814cfd5 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,3 +1,4 @@ +use crate::casts::utils; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::NumericLiteral; use clippy_utils::source::{SpanRangeExt, snippet_opt}; @@ -9,7 +10,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, Lit, Node, Path, QPath, TyKind, UnOp}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::adjustment::Adjust; -use rustc_middle::ty::{self, FloatTy, InferTy, Ty}; +use rustc_middle::ty::{self, Ty}; use rustc_span::{Symbol, sym}; use std::ops::ControlFlow; @@ -108,8 +109,8 @@ pub(super) fn check<'tcx>( && let Some(src) = cast_expr.span.get_source_text(cx) && cast_to.is_floating_point() && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) - && let from_nbits = 128 - n.get().leading_zeros() - && let to_nbits = fp_ty_mantissa_nbits(cast_to) + && let from_nbits = 128 - u64::from(n.get().leading_zeros()) + && let to_nbits = utils::float_ty_to_mantissa_nbits(cast_to) && from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits @@ -257,16 +258,6 @@ fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<&'e Lit> { } } -/// Returns the mantissa bits wide of a fp type. -/// Will return 0 if the type is not a fp -fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 { - match typ.kind() { - ty::Float(FloatTy::F32) => 23, - ty::Float(FloatTy::F64) | ty::Infer(InferTy::FloatVar(_)) => 52, - _ => 0, - } -} - /// Finds whether an `Expr` returns a type alias. /// /// TODO: Maybe we should move this to `clippy_utils` so others won't need to go down this dark, diff --git a/clippy_lints/src/casts/utils.rs b/clippy_lints/src/casts/utils.rs index 318a1646477e..442e5e28aa37 100644 --- a/clippy_lints/src/casts/utils.rs +++ b/clippy_lints/src/casts/utils.rs @@ -1,14 +1,55 @@ use clippy_utils::ty::{EnumValue, read_explicit_enum_value}; -use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TyCtxt, UintTy, VariantDiscr}; +use rustc_middle::ty::{self, AdtDef, FloatTy, 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 { - 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, +/// Returns the size in bits of an integral type. +/// Panics 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, + }, + _ => unreachable!(), + } +} + +/// Returns the size in bits of an floating type. +/// Panics if the type is not an float variant +pub(super) fn float_ty_to_nbits(typ: Ty<'_>) -> u64 { + match typ.kind() { + ty::Float(i) => match i { + FloatTy::F16 => 16, + FloatTy::F32 => 32, + FloatTy::F64 => 64, + FloatTy::F128 => 128, + }, + _ => unreachable!(), + } +} + +/// Returns the mantissa size in bits of an floating type. +/// Panics if the type is not an float variant +pub(super) fn float_ty_to_mantissa_nbits(typ: Ty<'_>) -> u64 { + match typ.kind() { + ty::Float(i) => match i { + FloatTy::F16 => 10, + FloatTy::F32 => 23, + FloatTy::F64 => 52, + FloatTy::F128 => 112, + }, + _ => unreachable!(), } }