Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions clippy_lints/src/neg_multiply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,10 @@ impl<'tcx> LateLintPass<'tcx> for NegMultiply {
}

fn check_mul(cx: &LateContext<'_>, mul_expr: &Expr<'_>, lit: &Expr<'_>, exp: &Expr<'_>) {
const F16_ONE: u16 = 1.0_f16.to_bits();
const F128_ONE: u128 = 1.0_f128.to_bits();
if let ExprKind::Lit(l) = lit.kind
&& matches!(
consts::lit_to_mir_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)),
Constant::Int(1)
| Constant::F16(F16_ONE)
| Constant::F32(1.0)
| Constant::F64(1.0)
| Constant::F128(F128_ONE)
Constant::Int(1) | Constant::F16(1.0) | Constant::F32(1.0) | Constant::F64(1.0) | Constant::F128(1.0)
)
&& cx.typeck_results().expr_ty(exp).is_numeric()
{
Expand Down
22 changes: 10 additions & 12 deletions clippy_lints/src/zero_div_zero.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::span_lint_and_help;
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::declare_lint_pass;

declare_clippy_lint! {
/// ### What it does
/// Checks for `0.0 / 0.0`.
///
/// ### Why is this bad?
/// It's less readable than `f32::NAN` or `f64::NAN`.
/// It's less readable than using the proper associated `NAN` constant.
///
/// ### Example
/// ```no_run
Expand All @@ -23,7 +24,7 @@ declare_clippy_lint! {
#[clippy::version = "pre 1.29.0"]
pub ZERO_DIVIDED_BY_ZERO,
complexity,
"usage of `0.0 / 0.0` to obtain NaN instead of `f32::NAN` or `f64::NAN`"
"usage of `0.0 / 0.0` to obtain NaN instead of using a predefined constant"
}

declare_lint_pass!(ZeroDiv => [ZERO_DIVIDED_BY_ZERO]);
Expand All @@ -40,23 +41,20 @@ impl<'tcx> LateLintPass<'tcx> for ZeroDiv {
&& let ctxt = expr.span.ctxt()
&& let Some(lhs_value) = ecx.eval_local(left, ctxt)
&& let Some(rhs_value) = ecx.eval_local(right, ctxt)
// FIXME(f16_f128): add these types when eq is available on all platforms
&& (Constant::F32(0.0) == lhs_value || Constant::F64(0.0) == lhs_value)
&& (Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value)
&& matches!(lhs_value, Constant::F16(0.0) | Constant::F32(0.0) | Constant::F64(0.0) | Constant::F128(0.0))
&& matches!(rhs_value, Constant::F16(0.0) | Constant::F32(0.0) | Constant::F64(0.0) | Constant::F128(0.0))
&& let ty::Float(float_ty) = cx.typeck_results().expr_ty(expr).kind()
{
// since we're about to suggest a use of f32::NAN or f64::NAN,
// match the precision of the literals that are given.
let float_type = match (lhs_value, rhs_value) {
(Constant::F64(_), _) | (_, Constant::F64(_)) => "f64",
_ => "f32",
};
span_lint_and_help(
cx,
ZERO_DIVIDED_BY_ZERO,
expr.span,
"constant division of `0.0` with `0.0` will always result in NaN",
None,
format!("consider using `{float_type}::NAN` if you would like a constant representing NaN",),
format!(
"consider using `{}::NAN` if you would like a constant representing NaN",
float_ty.name_str()
),
);
}
}
Expand Down
72 changes: 47 additions & 25 deletions clippy_utils/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{clip, is_direct_expn_of, sext, sym, unsext};

use rustc_abi::Size;
use rustc_apfloat::Float;
use rustc_apfloat::ieee::{Half, Quad};
use rustc_apfloat::ieee::Quad;
use rustc_ast::ast::{LitFloatType, LitKind};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, PatExpr, PatExprKind, QPath, TyKind, UnOp};
Expand Down Expand Up @@ -38,16 +38,14 @@ pub enum Constant {
Char(char),
/// An integer's bit representation.
Int(u128),
/// An `f16` bitcast to a `u16`.
// FIXME(f16_f128): use `f16` once builtins are available on all host tools platforms.
F16(u16),
/// An `f16`.
F16(f16),
/// An `f32`.
F32(f32),
/// An `f64`.
F64(f64),
/// An `f128` bitcast to a `u128`.
// FIXME(f16_f128): use `f128` once builtins are available on all host tools platforms.
F128(u128),
F128(f128),
/// `true` or `false`.
Bool(bool),
/// An array of constants.
Expand Down Expand Up @@ -169,8 +167,7 @@ impl Hash for Constant {
i.hash(state);
},
Self::F16(f) => {
// FIXME(f16_f128): once conversions to/from `f128` are available on all platforms,
f.hash(state);
f64::from(f).to_bits().hash(state);
},
Self::F32(f) => {
f64::from(f).to_bits().hash(state);
Expand All @@ -179,7 +176,7 @@ impl Hash for Constant {
f.to_bits().hash(state);
},
Self::F128(f) => {
f.hash(state);
f.to_bits().hash(state);
},
Self::Bool(b) => {
b.hash(state);
Expand Down Expand Up @@ -281,14 +278,9 @@ impl Constant {
self
}

fn parse_f16(s: &str) -> Self {
let f: Half = s.parse().unwrap();
Self::F16(f.to_bits().try_into().unwrap())
}

fn parse_f128(s: &str) -> Self {
let f: Quad = s.parse().unwrap();
Self::F128(f.to_bits())
Self::F128(f128::from_bits(f.to_bits()))
}

pub fn new_numeric_min<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Self> {
Expand Down Expand Up @@ -394,18 +386,20 @@ impl Constant {

pub fn is_pos_infinity(&self) -> bool {
match *self {
// FIXME(f16_f128): add f16 and f128 when constants are available
Constant::F16(x) => x == f16::INFINITY,
Constant::F32(x) => x == f32::INFINITY,
Constant::F64(x) => x == f64::INFINITY,
Constant::F128(x) => x == f128::INFINITY,
_ => false,
}
}

pub fn is_neg_infinity(&self) -> bool {
match *self {
// FIXME(f16_f128): add f16 and f128 when constants are available
Constant::F16(x) => x == f16::NEG_INFINITY,
Constant::F32(x) => x == f32::NEG_INFINITY,
Constant::F64(x) => x == f64::NEG_INFINITY,
Constant::F128(x) => x == f128::NEG_INFINITY,
_ => false,
}
}
Expand All @@ -420,14 +414,14 @@ pub fn lit_to_mir_constant(lit: &LitKind, ty: Option<Ty<'_>>) -> Constant {
LitKind::Char(c) => Constant::Char(c),
LitKind::Int(n, _) => Constant::Int(n.get()),
LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty {
// FIXME(f16_f128): just use `parse()` directly when available for `f16`/`f128`
FloatTy::F16 => Constant::parse_f16(is.as_str()),
FloatTy::F16 => Constant::F16(is.as_str().parse().unwrap()),
FloatTy::F32 => Constant::F32(is.as_str().parse().unwrap()),
FloatTy::F64 => Constant::F64(is.as_str().parse().unwrap()),
// FIXME(f16_f128): just use `parse()` directly when available for `f128`
FloatTy::F128 => Constant::parse_f128(is.as_str()),
},
LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind() {
ty::Float(FloatTy::F16) => Constant::parse_f16(is.as_str()),
ty::Float(FloatTy::F16) => Constant::F16(is.as_str().parse().unwrap()),
ty::Float(FloatTy::F32) => Constant::F32(is.as_str().parse().unwrap()),
ty::Float(FloatTy::F64) => Constant::F64(is.as_str().parse().unwrap()),
ty::Float(FloatTy::F128) => Constant::parse_f128(is.as_str()),
Expand Down Expand Up @@ -932,6 +926,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
}
}

#[expect(clippy::too_many_lines)]
fn binop(&self, op: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant> {
let l = self.expr(left)?;
let r = self.expr(right);
Expand Down Expand Up @@ -1003,7 +998,20 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
},
_ => None,
},
// FIXME(f16_f128): add these types when binary operations are available on all platforms
(Constant::F16(l), Some(Constant::F16(r))) => match op {
BinOpKind::Add => Some(Constant::F16(l + r)),
BinOpKind::Sub => Some(Constant::F16(l - r)),
BinOpKind::Mul => Some(Constant::F16(l * r)),
BinOpKind::Div => Some(Constant::F16(l / r)),
BinOpKind::Rem => Some(Constant::F16(l % r)),
BinOpKind::Eq => Some(Constant::Bool(l == r)),
BinOpKind::Ne => Some(Constant::Bool(l != r)),
BinOpKind::Lt => Some(Constant::Bool(l < r)),
BinOpKind::Le => Some(Constant::Bool(l <= r)),
BinOpKind::Ge => Some(Constant::Bool(l >= r)),
BinOpKind::Gt => Some(Constant::Bool(l > r)),
_ => None,
},
(Constant::F32(l), Some(Constant::F32(r))) => match op {
BinOpKind::Add => Some(Constant::F32(l + r)),
BinOpKind::Sub => Some(Constant::F32(l - r)),
Expand Down Expand Up @@ -1032,6 +1040,20 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
BinOpKind::Gt => Some(Constant::Bool(l > r)),
_ => None,
},
(Constant::F128(l), Some(Constant::F128(r))) => match op {
BinOpKind::Add => Some(Constant::F128(l + r)),
BinOpKind::Sub => Some(Constant::F128(l - r)),
BinOpKind::Mul => Some(Constant::F128(l * r)),
BinOpKind::Div => Some(Constant::F128(l / r)),
BinOpKind::Rem => Some(Constant::F128(l % r)),
BinOpKind::Eq => Some(Constant::Bool(l == r)),
BinOpKind::Ne => Some(Constant::Bool(l != r)),
BinOpKind::Lt => Some(Constant::Bool(l < r)),
BinOpKind::Le => Some(Constant::Bool(l <= r)),
BinOpKind::Ge => Some(Constant::Bool(l >= r)),
BinOpKind::Gt => Some(Constant::Bool(l > r)),
_ => None,
},
(l, r) => match (op, l, r) {
(BinOpKind::And, Constant::Bool(false), _) => Some(Constant::Bool(false)),
(BinOpKind::Or, Constant::Bool(true), _) => Some(Constant::Bool(true)),
Expand All @@ -1053,10 +1075,10 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, val: ConstValue, ty: Ty<'tcx>) -> O
(ConstValue::Scalar(Scalar::Int(int)), _) => match ty.kind() {
ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.to_bits(int.size()))),
ty::Float(FloatTy::F16) => Some(Constant::F16(int.into())),
ty::Float(FloatTy::F16) => Some(Constant::F16(f16::from_bits(int.into()))),
ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(int.into()))),
ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(int.into()))),
ty::Float(FloatTy::F128) => Some(Constant::F128(int.into())),
ty::Float(FloatTy::F128) => Some(Constant::F128(f128::from_bits(int.into()))),
ty::RawPtr(_, _) => Some(Constant::RawPtr(int.to_bits(int.size()))),
_ => None,
},
Expand All @@ -1076,10 +1098,10 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, val: ConstValue, ty: Ty<'tcx>) -> O
let range = alloc_range(offset + size * idx, size);
let val = alloc.read_scalar(&tcx, range, /* read_provenance */ false).ok()?;
res.push(match flt {
FloatTy::F16 => Constant::F16(val.to_u16().discard_err()?),
FloatTy::F16 => Constant::F16(f16::from_bits(val.to_u16().discard_err()?)),
FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().discard_err()?)),
FloatTy::F64 => Constant::F64(f64::from_bits(val.to_u64().discard_err()?)),
FloatTy::F128 => Constant::F128(val.to_u128().discard_err()?),
FloatTy::F128 => Constant::F128(f128::from_bits(val.to_u128().discard_err()?)),
});
}
Some(Constant::Vec(res))
Expand Down
2 changes: 2 additions & 0 deletions clippy_utils/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#![feature(box_patterns)]
#![feature(f128)]
#![feature(f16)]
#![feature(if_let_guard)]
#![feature(macro_metavar_expr)]
#![feature(never_type)]
Expand Down
2 changes: 0 additions & 2 deletions tests/ui/arithmetic_side_effects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,9 @@ pub fn association_with_structures_should_not_trigger_the_lint() {

pub fn hard_coded_allowed() {
let _ = 1f16 + 1f16;
//~^ arithmetic_side_effects
let _ = 1f32 + 1f32;
let _ = 1f64 + 1f64;
let _ = 1f128 + 1f128;
//~^ arithmetic_side_effects

let _ = Saturating(0u32) + Saturating(0u32);
let _ = String::new() + "";
Expand Down
Loading