diff --git a/CHANGELOG.md b/CHANGELOG.md index 55281f3cbec0..e58199b0cf90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6045,6 +6045,9 @@ Released 2018-09-13 [`enum-variant-name-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enum-variant-name-threshold [`enum-variant-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enum-variant-size-threshold [`excessive-nesting-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#excessive-nesting-threshold +[`float-cmp-allowed-constants`]: https://doc.rust-lang.org/clippy/lint_configuration.html#float-cmp-allowed-constants +[`float-cmp-ignore-change-detection`]: https://doc.rust-lang.org/clippy/lint_configuration.html#float-cmp-ignore-change-detection +[`float-cmp-ignore-constant-comparisons`]: https://doc.rust-lang.org/clippy/lint_configuration.html#float-cmp-ignore-constant-comparisons [`future-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#future-size-threshold [`ignore-interior-mutability`]: https://doc.rust-lang.org/clippy/lint_configuration.html#ignore-interior-mutability [`large-error-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#large-error-threshold diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index ce6cbc62a505..03b2e76ac4de 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -538,6 +538,59 @@ The maximum amount of nesting a block can reside in * [`excessive_nesting`](https://rust-lang.github.io/rust-clippy/master/index.html#excessive_nesting) +## `float-cmp-allowed-constants` +#### Example +```no_run +const VALUE: f64 = 1.0; +fn is_value(x: f64) -> bool { + // Will warn unless `crate_name::VALUE` is allowed + x == VALUE +} +``` + +**Default Value:** `[]` + +--- +**Affected lints:** +* [`float_cmp`](https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp) + + +## `float-cmp-ignore-change-detection` +#### Example +```no_run +fn f(x: f64) -> bool { + // Will warn if the config is `false` + x == x + 1.0 +} +``` + +**Default Value:** `true` + +--- +**Affected lints:** +* [`float_cmp`](https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp) + + +## `float-cmp-ignore-constant-comparisons` +#### Example +```no_run +const fn f(x: f64) -> f64 { + todo!() +} + +// Will warn if the config is `false` +if f(1.0) == f(2.0) { + // ... +} +``` + +**Default Value:** `true` + +--- +**Affected lints:** +* [`float_cmp`](https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp) + + ## `future-size-threshold` The maximum byte size a `Future` can have, before it triggers the `clippy::large_futures` lint diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 7eec3d4945a2..5ca4bbeaf563 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -645,6 +645,47 @@ define_Conf! { /// /// Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros. (warn_unsafe_macro_metavars_in_private_macros: bool = false), + /// Lint: FLOAT_CMP + /// + /// The list of constants which can be checked for exact equality. + /// + /// #### Example + /// ```no_run + /// const VALUE: f64 = 1.0; + /// fn is_value(x: f64) -> bool { + /// // Will warn unless `crate_name::VALUE` is allowed + /// x == VALUE + /// } + /// ``` + (float_cmp_allowed_constants: Vec = Vec::new()), + /// Lint: FLOAT_CMP + /// + /// Whether to ignore comparisons which have a constant result. + /// + /// #### Example + /// ```no_run + /// const fn f(x: f64) -> f64 { + /// todo!() + /// } + /// + /// // Will warn if the config is `false` + /// if f(1.0) == f(2.0) { + /// // ... + /// } + /// ``` + (float_cmp_ignore_constant_comparisons: bool = true), + /// Lint: FLOAT_CMP + /// + /// Whether to ignore comparisons which check if an operation changes the value of it's operand. + /// + /// #### Example + /// ```no_run + /// fn f(x: f64) -> bool { + /// // Will warn if the config is `false` + /// x == x + 1.0 + /// } + /// ``` + (float_cmp_ignore_change_detection: bool = true), } /// Search for the configuration file. diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index eabc67601a2f..7806421fbb65 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -570,7 +570,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::operators::ERASING_OP_INFO, crate::operators::FLOAT_ARITHMETIC_INFO, crate::operators::FLOAT_CMP_INFO, - crate::operators::FLOAT_CMP_CONST_INFO, crate::operators::FLOAT_EQUALITY_WITHOUT_ABS_INFO, crate::operators::IDENTITY_OP_INFO, crate::operators::IMPOSSIBLE_COMPARISONS_INFO, diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index a0900f46f6aa..f3ddb5403b57 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -241,3 +241,14 @@ declare_deprecated_lint! { pub MISMATCHED_TARGET_OS, "this lint has been replaced by `unexpected_cfgs`" } + +declare_deprecated_lint! { + /// ### What it does + /// Nothing. This lint has been deprecated. + /// + /// ### Deprecation reason + /// `float_cmp` handles this via config options + #[clippy::version = "1.76.0"] + pub FLOAT_CMP_CONST, + "`float_cmp` handles this via config options" +} diff --git a/clippy_lints/src/lib.deprecated.rs b/clippy_lints/src/lib.deprecated.rs index 0d21261822dd..be7c74e36ab1 100644 --- a/clippy_lints/src/lib.deprecated.rs +++ b/clippy_lints/src/lib.deprecated.rs @@ -75,4 +75,8 @@ "clippy::mismatched_target_os", "this lint has been replaced by `unexpected_cfgs`", ); + store.register_removed( + "clippy::float_cmp_const", + "`float_cmp` handles this via config options", + ); } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 25cd76104007..d284d1f4eeb6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -816,7 +816,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(conf))); store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(conf))); store.register_late_pass(move |_| Box::new(manual_rotate::ManualRotate)); - store.register_late_pass(move |_| Box::new(operators::Operators::new(conf))); + store.register_late_pass(move |tcx| Box::new(operators::Operators::new(tcx, conf))); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(conf))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); diff --git a/clippy_lints/src/operators/float_cmp.rs b/clippy_lints/src/operators/float_cmp.rs index 0e5b440c50f2..5544a661e834 100644 --- a/clippy_lints/src/operators/float_cmp.rs +++ b/clippy_lints/src/operators/float_cmp.rs @@ -1,47 +1,93 @@ -use clippy_utils::consts::{constant_with_source, Constant}; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::get_item_name; use clippy_utils::sugg::Sugg; +use clippy_utils::visitors::{for_each_expr_without_closures, is_const_evaluatable}; +use clippy_utils::{get_item_name, get_named_const_def_id, path_res, peel_hir_expr_while, SpanlessEq}; +use core::ops::ControlFlow; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; +use rustc_hir::def::Res; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, Safety, UnOp}; use rustc_lint::LateContext; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty, TypeFlags, TypeVisitableExt}; -use super::{FLOAT_CMP, FLOAT_CMP_CONST}; +use super::{FloatCmpConfig, FLOAT_CMP}; pub(crate) fn check<'tcx>( cx: &LateContext<'tcx>, + config: &FloatCmpConfig, expr: &'tcx Expr<'_>, op: BinOpKind, left: &'tcx Expr<'_>, right: &'tcx Expr<'_>, ) { - if (op == BinOpKind::Eq || op == BinOpKind::Ne) && is_float(cx, left) { - let left_is_local = match constant_with_source(cx, cx.typeck_results(), left) { - Some((c, s)) if !is_allowed(&c) => s.is_local(), - Some(_) => return, - None => true, - }; - let right_is_local = match constant_with_source(cx, cx.typeck_results(), right) { - Some((c, s)) if !is_allowed(&c) => s.is_local(), - Some(_) => return, - None => true, - }; + let peel_expr = |e: &'tcx Expr<'tcx>| match e.kind { + ExprKind::Cast(e, _) | ExprKind::AddrOf(BorrowKind::Ref, _, e) | ExprKind::Unary(UnOp::Neg, e) => Some(e), + _ => None, + }; + if matches!(op, BinOpKind::Eq | BinOpKind::Ne) + && let left_reduced = peel_hir_expr_while(left, peel_expr) + && let right_reduced = peel_hir_expr_while(right, peel_expr) + && is_float(cx, left_reduced) + && is_float(cx, right_reduced) + // Don't lint literal comparisons + && let is_left_lit = matches!(left_reduced.kind, ExprKind::Lit(_)) + && let is_right_lit = matches!(right_reduced.kind, ExprKind::Lit(_)) + && !(is_left_lit && is_right_lit) // Allow comparing the results of signum() - if is_signum(cx, left) && is_signum(cx, right) { + && !(is_signum(cx, left_reduced) && is_signum(cx, right_reduced)) + && match (path_res(cx, left_reduced), path_res(cx, right_reduced)) { + (Res::Err, _) | (_, Res::Err) => true, + (left, right) => left != right, + } + { + let left_c = constant(cx, cx.typeck_results(), left_reduced); + let is_left_const = left_c.is_some(); + if left_c.is_some_and(|c| is_allowed(&c)) { + return; + } + let right_c = constant(cx, cx.typeck_results(), right_reduced); + let is_right_const = right_c.is_some(); + if right_c.is_some_and(|c| is_allowed(&c)) { + return; + } + + if config.ignore_constant_comparisons + && (is_left_const || is_const_evaluatable(cx, left_reduced)) + && (is_right_const || is_const_evaluatable(cx, right_reduced)) + { + return; + } + + // Neither `CONST == lit` or `ALLOWED_CONST == x` should lint. + if get_named_const_def_id(cx, left_reduced) + .is_some_and(|id| is_right_lit || config.allowed_constants.contains(&id)) + || get_named_const_def_id(cx, right_reduced) + .is_some_and(|id| is_left_lit || config.allowed_constants.contains(&id)) + { + return; + } + + if config.ignore_change_detection + && ((is_pure_expr(cx, left_reduced) && contains_expr(cx, right, left)) + || (is_pure_expr(cx, right_reduced) && contains_expr(cx, left, right))) + { return; } if let Some(name) = get_item_name(cx, expr) { let name = name.as_str(); - if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") || name.ends_with("_eq") { + if name == "eq" || name == "ne" || name.starts_with("eq_") || name.ends_with("_eq") { return; } } - let is_comparing_arrays = is_array(cx, left) || is_array(cx, right); - let (lint, msg) = get_lint_and_message(left_is_local && right_is_local, is_comparing_arrays); - span_lint_and_then(cx, lint, expr.span, msg, |diag| { + let is_comparing_arrays = is_array(cx, left_reduced) || is_array(cx, right_reduced); + let msg = if is_comparing_arrays { + "strict comparison of `f32` or `f64` arrays" + } else { + "strict comparison of `f32` or `f64`" + }; + span_lint_and_then(cx, FLOAT_CMP, expr.span, msg, |diag| { let lhs = Sugg::hir(cx, left, ".."); let rhs = Sugg::hir(cx, right, ".."); @@ -61,54 +107,105 @@ pub(crate) fn check<'tcx>( } } -fn get_lint_and_message(is_local: bool, is_comparing_arrays: bool) -> (&'static rustc_lint::Lint, &'static str) { - if is_local { - ( - FLOAT_CMP, - if is_comparing_arrays { - "strict comparison of `f32` or `f64` arrays" - } else { - "strict comparison of `f32` or `f64`" - }, - ) - } else { - ( - FLOAT_CMP_CONST, - if is_comparing_arrays { - "strict comparison of `f32` or `f64` constant arrays" - } else { - "strict comparison of `f32` or `f64` constant" - }, - ) - } -} - fn is_allowed(val: &Constant<'_>) -> bool { match val { // FIXME(f16_f128): add when equality check is available on all platforms + Constant::Ref(val) => is_allowed(val), &Constant::F32(f) => f == 0.0 || f.is_infinite(), &Constant::F64(f) => f == 0.0 || f.is_infinite(), - Constant::Vec(vec) => vec.iter().all(|f| match f { - Constant::F32(f) => *f == 0.0 || (*f).is_infinite(), - Constant::F64(f) => *f == 0.0 || (*f).is_infinite(), + Constant::Vec(vec) => vec.iter().all(|f| match *f { + Constant::F32(f) => f == 0.0 || f.is_infinite(), + Constant::F64(f) => f == 0.0 || f.is_infinite(), _ => false, }), + Constant::Repeat(val, _) => match **val { + Constant::F32(f) => f == 0.0 || f.is_infinite(), + Constant::F64(f) => f == 0.0 || f.is_infinite(), + _ => false, + }, _ => false, } } -// Return true if `expr` is the result of `signum()` invoked on a float value. -fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - // The negation of a signum is still a signum - if let ExprKind::Unary(UnOp::Neg, child_expr) = expr.kind { - return is_signum(cx, child_expr); +// This is a best effort guess and may have false positives and negatives. +fn is_pure_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { + match e.kind { + ExprKind::Path(_) | ExprKind::Lit(_) => true, + ExprKind::Field(e, _) | ExprKind::Cast(e, _) | ExprKind::Repeat(e, _) => is_pure_expr(cx, e), + ExprKind::Tup(args) => args.iter().all(|arg| is_pure_expr(cx, arg)), + ExprKind::Struct(_, fields, base) => { + base.map_or(true, |base| is_pure_expr(cx, base)) && fields.iter().all(|f| is_pure_expr(cx, f.expr)) + }, + + // Since rust doesn't actually have the concept of a pure function we + // have to guess whether it's likely pure from the signature of the + // function. + ExprKind::Unary(_, e) => is_pure_arg_ty(cx, cx.typeck_results().expr_ty_adjusted(e)) && is_pure_expr(cx, e), + ExprKind::Binary(_, x, y) | ExprKind::Index(x, y, _) => { + is_pure_arg_ty(cx, cx.typeck_results().expr_ty_adjusted(x)) + && is_pure_arg_ty(cx, cx.typeck_results().expr_ty_adjusted(y)) + && is_pure_expr(cx, x) + && is_pure_expr(cx, y) + }, + ExprKind::MethodCall(_, recv, args, _) => { + is_pure_arg_ty(cx, cx.typeck_results().expr_ty_adjusted(recv)) + && is_pure_expr(cx, recv) + && cx + .typeck_results() + .type_dependent_def_id(e.hir_id) + .is_some_and(|did| matches!(cx.tcx.fn_sig(did).skip_binder().skip_binder().safety, Safety::Safe)) + && args + .iter() + .all(|arg| is_pure_arg_ty(cx, cx.typeck_results().expr_ty_adjusted(arg)) && is_pure_expr(cx, arg)) + }, + ExprKind::Call(f, args @ [_, ..]) => { + is_pure_expr(cx, f) + && is_pure_fn_ty(cx, cx.typeck_results().expr_ty_adjusted(f)) + && args + .iter() + .all(|arg| is_pure_arg_ty(cx, cx.typeck_results().expr_ty_adjusted(arg)) && is_pure_expr(cx, arg)) + }, + + _ => false, } +} + +fn is_pure_fn_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + let sig = match *ty.peel_refs().kind() { + ty::FnDef(did, _) => cx.tcx.fn_sig(did).skip_binder(), + ty::FnPtr(sig) => sig, + ty::Closure(_, args) => { + return args.as_closure().upvar_tys().iter().all(|ty| is_pure_arg_ty(cx, ty)); + }, + _ => return false, + }; + matches!(sig.skip_binder().safety, Safety::Safe) +} +fn is_pure_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + !ty.is_mutable_ptr() + && ty.is_copy_modulo_regions(cx.tcx, cx.param_env) + && (ty.peel_refs().is_freeze(cx.tcx, cx.param_env) + || !ty.has_type_flags(TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_RE_ERASED | TypeFlags::HAS_RE_BOUND)) +} + +fn contains_expr<'tcx>(cx: &LateContext<'tcx>, corpus: &'tcx Expr<'tcx>, e: &'tcx Expr<'tcx>) -> bool { + for_each_expr_without_closures(corpus, |corpus| { + if SpanlessEq::new(cx).eq_expr(corpus, e) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_some() +} + +// Return true if `expr` is the result of `signum()` invoked on a float value. +fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind && sym!(signum) == method_name.ident.name - // Check that the receiver of the signum() is a float (expressions[0] is the receiver of - // the method call) { + // Check that the receiver of the signum() is a float return is_float(cx, self_arg); } false diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index 9baecff801f2..43488c6064d4 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -24,8 +24,11 @@ mod verbose_bit_mask; pub(crate) mod arithmetic_side_effects; use clippy_config::Conf; +use clippy_utils::def_path_def_ids; +use rustc_hir::def_id::DefIdSet; use rustc_hir::{Body, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; declare_clippy_lint! { @@ -633,70 +636,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for (in-)equality comparisons on constant floating-point - /// values (apart from zero), except in functions called `*eq*` (which probably - /// implement equality for a type involving floats). - /// - /// ### Why restrict this? - /// Floating point calculations are usually imprecise, so asking if two values are *exactly* - /// equal is asking for trouble because arriving at the same logical result via different - /// routes (e.g. calculation versus constant) may yield different values. - /// - /// ### Example - /// - /// ```no_run - /// let a: f64 = 1000.1; - /// let b: f64 = 0.2; - /// let x = a + b; - /// const Y: f64 = 1000.3; // Expected value. - /// - /// // Actual value: 1000.3000000000001 - /// println!("{x}"); - /// - /// let are_equal = x == Y; - /// println!("{are_equal}"); // false - /// ``` - /// - /// The correct way to compare floating point numbers is to define an allowed error margin. This - /// may be challenging if there is no "natural" error margin to permit. Broadly speaking, there - /// are two cases: - /// - /// 1. If your values are in a known range and you can define a threshold for "close enough to - /// be equal", it may be appropriate to define an absolute error margin. For example, if your - /// data is "length of vehicle in centimeters", you may consider 0.1 cm to be "close enough". - /// 1. If your code is more general and you do not know the range of values, you should use a - /// relative error margin, accepting e.g. 0.1% of error regardless of specific values. - /// - /// For the scenario where you can define a meaningful absolute error margin, consider using: - /// - /// ```no_run - /// let a: f64 = 1000.1; - /// let b: f64 = 0.2; - /// let x = a + b; - /// const Y: f64 = 1000.3; // Expected value. - /// - /// const ALLOWED_ERROR_VEHICLE_LENGTH_CM: f64 = 0.1; - /// let within_tolerance = (x - Y).abs() < ALLOWED_ERROR_VEHICLE_LENGTH_CM; - /// println!("{within_tolerance}"); // true - /// ``` - /// - /// NB! Do not use `f64::EPSILON` - while the error margin is often called "epsilon", this is - /// a different use of the term that is not suitable for floating point equality comparison. - /// Indeed, for the example above using `f64::EPSILON` as the allowed error would return `false`. - /// - /// For the scenario where no meaningful absolute error can be defined, refer to - /// [the floating point guide](https://www.floating-point-gui.de/errors/comparison) - /// for a reference implementation of relative error based comparison of floating point values. - /// `MIN_NORMAL` in the reference implementation is equivalent to `MIN_POSITIVE` in Rust. - #[clippy::version = "pre 1.29.0"] - pub FLOAT_CMP_CONST, - restriction, - "using `==` or `!=` on float constants instead of comparing difference with an allowed error" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for getting the remainder of integer division by one or minus + /// Checks for getting the remainder of a division by one or minus /// one. /// /// ### Why is this bad? @@ -837,17 +777,33 @@ declare_clippy_lint! { "explicit self-assignment" } +struct FloatCmpConfig { + allowed_constants: DefIdSet, + ignore_constant_comparisons: bool, + ignore_change_detection: bool, +} + pub struct Operators { arithmetic_context: numeric_arithmetic::Context, verbose_bit_mask_threshold: u64, modulo_arithmetic_allow_comparison_to_zero: bool, + float_cmp_config: FloatCmpConfig, } impl Operators { - pub fn new(conf: &'static Conf) -> Self { + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { Self { arithmetic_context: numeric_arithmetic::Context::default(), verbose_bit_mask_threshold: conf.verbose_bit_mask_threshold, modulo_arithmetic_allow_comparison_to_zero: conf.allow_comparison_to_zero, + float_cmp_config: FloatCmpConfig { + allowed_constants: conf + .float_cmp_allowed_constants + .iter() + .flat_map(|x| def_path_def_ids(tcx, &x.split("::").collect::>())) + .collect(), + ignore_constant_comparisons: conf.float_cmp_ignore_constant_comparisons, + ignore_change_detection: conf.float_cmp_ignore_change_detection, + }, } } } @@ -873,14 +829,12 @@ impl_lint_pass!(Operators => [ INTEGER_DIVISION, CMP_OWNED, FLOAT_CMP, - FLOAT_CMP_CONST, MODULO_ONE, MODULO_ARITHMETIC, NEEDLESS_BITWISE_BOOL, PTR_EQ, SELF_ASSIGNMENT, ]); - impl<'tcx> LateLintPass<'tcx> for Operators { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { eq_op::check_assert(cx, e); @@ -906,7 +860,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators { float_equality_without_abs::check(cx, e, op.node, lhs, rhs); integer_division::check(cx, e, op.node, lhs, rhs); cmp_owned::check(cx, op.node, lhs, rhs); - float_cmp::check(cx, e, op.node, lhs, rhs); + float_cmp::check(cx, &self.float_cmp_config, e, op.node, lhs, rhs); modulo_one::check(cx, e, op.node, rhs); modulo_arithmetic::check( cx, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 476370133aee..3bbf64708706 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -245,6 +245,17 @@ pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool { } } +/// If the expression is path to either a constant or an associated constant get the `DefId`. +pub fn get_named_const_def_id<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option { + if let ExprKind::Path(p) = &e.kind + && let Res::Def(DefKind::Const | DefKind::AssocConst, id) = cx.qpath_res(p, e.hir_id) + { + Some(id) + } else { + None + } +} + /// Checks if a `Res` refers to a constructor of a `LangItem` /// For example, use this to check whether a function call or a pattern is `Some(..)`. pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool { diff --git a/tests/ui-toml/float_cmp/change_detect/clippy.toml b/tests/ui-toml/float_cmp/change_detect/clippy.toml new file mode 100644 index 000000000000..65e32118cb09 --- /dev/null +++ b/tests/ui-toml/float_cmp/change_detect/clippy.toml @@ -0,0 +1,6 @@ +float-cmp-ignore-change-detection = false +float-cmp-allowed-constants = [ + "core::f32::EPSILON", + "f32::EPSILON", + "test::F32_ARRAY", +] diff --git a/tests/ui-toml/float_cmp/const_cmp/clippy.toml b/tests/ui-toml/float_cmp/const_cmp/clippy.toml new file mode 100644 index 000000000000..c7521206a899 --- /dev/null +++ b/tests/ui-toml/float_cmp/const_cmp/clippy.toml @@ -0,0 +1,6 @@ +float-cmp-ignore-constant-comparisons = false +float-cmp-allowed-constants = [ + "core::f32::EPSILON", + "f32::EPSILON", + "test::F32_ARRAY", +] diff --git a/tests/ui-toml/float_cmp/named_const/clippy.toml b/tests/ui-toml/float_cmp/named_const/clippy.toml new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/ui-toml/float_cmp/test.change_detect.stderr b/tests/ui-toml/float_cmp/test.change_detect.stderr new file mode 100644 index 000000000000..49da8754d72f --- /dev/null +++ b/tests/ui-toml/float_cmp/test.change_detect.stderr @@ -0,0 +1,296 @@ +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:22:21 + | +LL | let _ = x == y; + | ^^^^^^ help: consider comparing them within some margin of error: `(x - y).abs() < error_margin` + | +note: the lint level is defined here + --> tests/ui-toml/float_cmp/test.rs:9:9 + | +LL | #![deny(clippy::float_cmp)] + | ^^^^^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:23:21 + | +LL | let _ = x != y; + | ^^^^^^ help: consider comparing them within some margin of error: `(x - y).abs() > error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:24:21 + | +LL | let _ = x == 5.5; + | ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 5.5).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:25:21 + | +LL | let _ = 5.5 == x; + | ^^^^^^^^ help: consider comparing them within some margin of error: `(5.5 - x).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:48:21 + | +LL | let _ = x == y; + | ^^^^^^ help: consider comparing them within some margin of error: `(x - y).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:49:21 + | +LL | let _ = x != y; + | ^^^^^^ help: consider comparing them within some margin of error: `(x - y).abs() > error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:50:21 + | +LL | let _ = x == 5.5; + | ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 5.5).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:51:21 + | +LL | let _ = 5.5 == x; + | ^^^^^^^^ help: consider comparing them within some margin of error: `(5.5 - x).abs() < error_margin` + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:74:21 + | +LL | let _ = x == y; + | ^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:75:21 + | +LL | let _ = x == [5.5; 4]; + | ^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:76:21 + | +LL | let _ = [5.5; 4] == x; + | ^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:77:21 + | +LL | let _ = [0.0, 0.0, 0.0, 5.5] == x; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:78:21 + | +LL | let _ = x == [0.0, 0.0, 0.0, 5.5]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:94:21 + | +LL | let _ = x == y; + | ^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:95:21 + | +LL | let _ = x == [5.5; 4]; + | ^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:96:21 + | +LL | let _ = [5.5; 4] == x; + | ^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:97:21 + | +LL | let _ = [0.0, 0.0, 0.0, 5.5] == x; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:98:21 + | +LL | let _ = x == [0.0, 0.0, 0.0, 5.5]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:116:21 + | +LL | let _ = x == y; + | ^^^^^^ help: consider comparing them within some margin of error: `(x - y).abs() < error_margin` + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:122:21 + | +LL | let _ = x == y; + | ^^^^^^ + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:137:21 + | +LL | let _ = f32::EPSILON * x == x * x; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(f32::EPSILON * x - x * x).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:138:21 + | +LL | let _ = x * x == f32::EPSILON * x; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x * x - f32::EPSILON * x).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:163:17 + | +LL | let _ = f(1.0) == f(5.0); + | ^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(f(1.0) - f(5.0)).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:164:17 + | +LL | let _ = 1.0 == f(5.0); + | ^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(1.0 - f(5.0)).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:165:17 + | +LL | let _ = f(1.0) + 1.0 != 5.0; + | ^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(f(1.0) + 1.0 - 5.0).abs() > error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:207:21 + | +LL | let _ = x == C[1]; + | ^^^^^^^^^ help: consider comparing them within some margin of error: `(x - C[1]).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:208:21 + | +LL | let _ = C[1] == x; + | ^^^^^^^^^ help: consider comparing them within some margin of error: `(C[1] - x).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:273:21 + | +LL | let _ = x == x + 1.0; + | ^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x - (x + 1.0)).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:274:21 + | +LL | let _ = x + 1.0 == x; + | ^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x + 1.0 - x).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:275:21 + | +LL | let _ = -x == -x + 1.0; + | ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(-x - (-x + 1.0)).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:276:21 + | +LL | let _ = -x + 1.0 == -x; + | ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(-x + 1.0 - -x).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:277:21 + | +LL | let _ = x == f1(x); + | ^^^^^^^^^^ help: consider comparing them within some margin of error: `(x - f1(x)).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:278:21 + | +LL | let _ = f1(x) == x; + | ^^^^^^^^^^ help: consider comparing them within some margin of error: `(f1(x) - x).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:279:21 + | +LL | let _ = x == f2(x, y); + | ^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x - f2(x, y)).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:280:21 + | +LL | let _ = f2(x, y) == x; + | ^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(f2(x, y) - x).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:281:21 + | +LL | let _ = f1(f1(x)) == f1(x); + | ^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(f1(f1(x)) - f1(x)).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:282:21 + | +LL | let _ = f1(x) == f1(f1(x)); + | ^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(f1(x) - f1(f1(x))).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:285:21 + | +LL | let _ = z.0 == z.0 + 1.0; + | ^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(z.0 - (z.0 + 1.0)).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:286:21 + | +LL | let _ = z.0 + 1.0 == z.0; + | ^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(z.0 + 1.0 - z.0).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:290:21 + | +LL | let _ = *x + 1.0 == *x; + | ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(*x + 1.0 - *x).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:291:21 + | +LL | let _ = *x == *x + 1.0; + | ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(*x - (*x + 1.0)).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:292:21 + | +LL | let _ = *x == f1(*x); + | ^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(*x - f1(*x)).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:293:21 + | +LL | let _ = f1(*x) == *x; + | ^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(f1(*x) - *x).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:298:21 + | +LL | let _ = x.next().unwrap() == x.next().unwrap() + 1.0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x.next().unwrap() - (x.next().unwrap() + 1.0)).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:314:21 + | +LL | let _ = x.f() + 1.0 == x.f(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x.f() + 1.0 - x.f()).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:315:21 + | +LL | let _ = x.f() == x.f() + 1.0; + | ^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x.f() - (x.f() + 1.0)).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:320:17 + | +LL | let _ = f(1.0) == f(1.0) + 1.0; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(f(1.0) - (f(1.0) + 1.0)).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:324:17 + | +LL | let _ = f(1.0) == f(1.0) + 1.0; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(f(1.0) - (f(1.0) + 1.0)).abs() < error_margin` + +error: aborting due to 48 previous errors + diff --git a/tests/ui-toml/float_cmp/test.const_cmp.stderr b/tests/ui-toml/float_cmp/test.const_cmp.stderr new file mode 100644 index 000000000000..cf50d04eab7d --- /dev/null +++ b/tests/ui-toml/float_cmp/test.const_cmp.stderr @@ -0,0 +1,212 @@ +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:22:21 + | +LL | let _ = x == y; + | ^^^^^^ help: consider comparing them within some margin of error: `(x - y).abs() < error_margin` + | +note: the lint level is defined here + --> tests/ui-toml/float_cmp/test.rs:9:9 + | +LL | #![deny(clippy::float_cmp)] + | ^^^^^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:23:21 + | +LL | let _ = x != y; + | ^^^^^^ help: consider comparing them within some margin of error: `(x - y).abs() > error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:24:21 + | +LL | let _ = x == 5.5; + | ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 5.5).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:25:21 + | +LL | let _ = 5.5 == x; + | ^^^^^^^^ help: consider comparing them within some margin of error: `(5.5 - x).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:48:21 + | +LL | let _ = x == y; + | ^^^^^^ help: consider comparing them within some margin of error: `(x - y).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:49:21 + | +LL | let _ = x != y; + | ^^^^^^ help: consider comparing them within some margin of error: `(x - y).abs() > error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:50:21 + | +LL | let _ = x == 5.5; + | ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 5.5).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:51:21 + | +LL | let _ = 5.5 == x; + | ^^^^^^^^ help: consider comparing them within some margin of error: `(5.5 - x).abs() < error_margin` + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:74:21 + | +LL | let _ = x == y; + | ^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:75:21 + | +LL | let _ = x == [5.5; 4]; + | ^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:76:21 + | +LL | let _ = [5.5; 4] == x; + | ^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:77:21 + | +LL | let _ = [0.0, 0.0, 0.0, 5.5] == x; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:78:21 + | +LL | let _ = x == [0.0, 0.0, 0.0, 5.5]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:94:21 + | +LL | let _ = x == y; + | ^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:95:21 + | +LL | let _ = x == [5.5; 4]; + | ^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:96:21 + | +LL | let _ = [5.5; 4] == x; + | ^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:97:21 + | +LL | let _ = [0.0, 0.0, 0.0, 5.5] == x; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:98:21 + | +LL | let _ = x == [0.0, 0.0, 0.0, 5.5]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:116:21 + | +LL | let _ = x == y; + | ^^^^^^ help: consider comparing them within some margin of error: `(x - y).abs() < error_margin` + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:122:21 + | +LL | let _ = x == y; + | ^^^^^^ + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:137:21 + | +LL | let _ = f32::EPSILON * x == x * x; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(f32::EPSILON * x - x * x).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:138:21 + | +LL | let _ = x * x == f32::EPSILON * x; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x * x - f32::EPSILON * x).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:155:17 + | +LL | let _ = f(1.0) == f(5.0); + | ^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(f(1.0) - f(5.0)).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:156:17 + | +LL | let _ = 1.0 == f(5.0); + | ^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(1.0 - f(5.0)).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:157:17 + | +LL | let _ = f(1.0) + 1.0 != 5.0; + | ^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(f(1.0) + 1.0 - 5.0).abs() > error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:163:17 + | +LL | let _ = f(1.0) == f(5.0); + | ^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(f(1.0) - f(5.0)).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:164:17 + | +LL | let _ = 1.0 == f(5.0); + | ^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(1.0 - f(5.0)).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:165:17 + | +LL | let _ = f(1.0) + 1.0 != 5.0; + | ^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(f(1.0) + 1.0 - 5.0).abs() > error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:207:21 + | +LL | let _ = x == C[1]; + | ^^^^^^^^^ help: consider comparing them within some margin of error: `(x - C[1]).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:208:21 + | +LL | let _ = C[1] == x; + | ^^^^^^^^^ help: consider comparing them within some margin of error: `(C[1] - x).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:298:21 + | +LL | let _ = x.next().unwrap() == x.next().unwrap() + 1.0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x.next().unwrap() - (x.next().unwrap() + 1.0)).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:314:21 + | +LL | let _ = x.f() + 1.0 == x.f(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x.f() + 1.0 - x.f()).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:315:21 + | +LL | let _ = x.f() == x.f() + 1.0; + | ^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x.f() - (x.f() + 1.0)).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:324:17 + | +LL | let _ = f(1.0) == f(1.0) + 1.0; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(f(1.0) - (f(1.0) + 1.0)).abs() < error_margin` + +error: aborting due to 34 previous errors + diff --git a/tests/ui-toml/float_cmp/test.named_const.stderr b/tests/ui-toml/float_cmp/test.named_const.stderr new file mode 100644 index 000000000000..20f041ec61c9 --- /dev/null +++ b/tests/ui-toml/float_cmp/test.named_const.stderr @@ -0,0 +1,254 @@ +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:22:21 + | +LL | let _ = x == y; + | ^^^^^^ help: consider comparing them within some margin of error: `(x - y).abs() < error_margin` + | +note: the lint level is defined here + --> tests/ui-toml/float_cmp/test.rs:9:9 + | +LL | #![deny(clippy::float_cmp)] + | ^^^^^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:23:21 + | +LL | let _ = x != y; + | ^^^^^^ help: consider comparing them within some margin of error: `(x - y).abs() > error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:24:21 + | +LL | let _ = x == 5.5; + | ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 5.5).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:25:21 + | +LL | let _ = 5.5 == x; + | ^^^^^^^^ help: consider comparing them within some margin of error: `(5.5 - x).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:48:21 + | +LL | let _ = x == y; + | ^^^^^^ help: consider comparing them within some margin of error: `(x - y).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:49:21 + | +LL | let _ = x != y; + | ^^^^^^ help: consider comparing them within some margin of error: `(x - y).abs() > error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:50:21 + | +LL | let _ = x == 5.5; + | ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 5.5).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:51:21 + | +LL | let _ = 5.5 == x; + | ^^^^^^^^ help: consider comparing them within some margin of error: `(5.5 - x).abs() < error_margin` + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:74:21 + | +LL | let _ = x == y; + | ^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:75:21 + | +LL | let _ = x == [5.5; 4]; + | ^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:76:21 + | +LL | let _ = [5.5; 4] == x; + | ^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:77:21 + | +LL | let _ = [0.0, 0.0, 0.0, 5.5] == x; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:78:21 + | +LL | let _ = x == [0.0, 0.0, 0.0, 5.5]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:94:21 + | +LL | let _ = x == y; + | ^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:95:21 + | +LL | let _ = x == [5.5; 4]; + | ^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:96:21 + | +LL | let _ = [5.5; 4] == x; + | ^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:97:21 + | +LL | let _ = [0.0, 0.0, 0.0, 5.5] == x; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:98:21 + | +LL | let _ = x == [0.0, 0.0, 0.0, 5.5]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:116:21 + | +LL | let _ = x == y; + | ^^^^^^ help: consider comparing them within some margin of error: `(x - y).abs() < error_margin` + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:122:21 + | +LL | let _ = x == y; + | ^^^^^^ + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:130:21 + | +LL | let _ = x == f32::EPSILON; + | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x - f32::EPSILON).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:131:21 + | +LL | let _ = f32::EPSILON == x; + | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(f32::EPSILON - x).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:132:21 + | +LL | let _ = &&x == &&core::f32::EPSILON; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(&&x - &&core::f32::EPSILON).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:133:21 + | +LL | let _ = &&core::f32::EPSILON == &&x; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(&&core::f32::EPSILON - &&x).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:134:21 + | +LL | let _ = y == f32::EPSILON as f64; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(y - f32::EPSILON as f64).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:135:21 + | +LL | let _ = f32::EPSILON as f64 == y; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(f32::EPSILON as f64 - y).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:137:21 + | +LL | let _ = f32::EPSILON * x == x * x; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(f32::EPSILON * x - x * x).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:138:21 + | +LL | let _ = x * x == f32::EPSILON * x; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x * x - f32::EPSILON * x).abs() < error_margin` + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:143:21 + | +LL | let _ = x == F32_ARRAY; + | ^^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:144:21 + | +LL | let _ = F32_ARRAY == x; + | ^^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:145:21 + | +LL | let _ = &&x == &&F32_ARRAY; + | ^^^^^^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` arrays + --> tests/ui-toml/float_cmp/test.rs:146:21 + | +LL | let _ = &&F32_ARRAY == &&x; + | ^^^^^^^^^^^^^^^^^^ + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:163:17 + | +LL | let _ = f(1.0) == f(5.0); + | ^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(f(1.0) - f(5.0)).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:164:17 + | +LL | let _ = 1.0 == f(5.0); + | ^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(1.0 - f(5.0)).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:165:17 + | +LL | let _ = f(1.0) + 1.0 != 5.0; + | ^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(f(1.0) + 1.0 - 5.0).abs() > error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:207:21 + | +LL | let _ = x == C[1]; + | ^^^^^^^^^ help: consider comparing them within some margin of error: `(x - C[1]).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:208:21 + | +LL | let _ = C[1] == x; + | ^^^^^^^^^ help: consider comparing them within some margin of error: `(C[1] - x).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:298:21 + | +LL | let _ = x.next().unwrap() == x.next().unwrap() + 1.0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x.next().unwrap() - (x.next().unwrap() + 1.0)).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:314:21 + | +LL | let _ = x.f() + 1.0 == x.f(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x.f() + 1.0 - x.f()).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:315:21 + | +LL | let _ = x.f() == x.f() + 1.0; + | ^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x.f() - (x.f() + 1.0)).abs() < error_margin` + +error: strict comparison of `f32` or `f64` + --> tests/ui-toml/float_cmp/test.rs:324:17 + | +LL | let _ = f(1.0) == f(1.0) + 1.0; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(f(1.0) - (f(1.0) + 1.0)).abs() < error_margin` + +error: aborting due to 41 previous errors + diff --git a/tests/ui-toml/float_cmp/test.rs b/tests/ui-toml/float_cmp/test.rs new file mode 100644 index 000000000000..75d8d43a684c --- /dev/null +++ b/tests/ui-toml/float_cmp/test.rs @@ -0,0 +1,346 @@ +//@no-rustfix +//@revisions: change_detect const_cmp named_const +//@[change_detect] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/float_cmp/change_detect +//@[const_cmp] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/float_cmp/const_cmp +//@[named_const] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/float_cmp/named_const + +// FIXME(f16_f128): const casting is not yet supported for these types. Add when available. + +#![deny(clippy::float_cmp)] +#![allow( + clippy::op_ref, + clippy::eq_op, + clippy::legacy_numeric_constants, + clippy::excessive_precision +)] + +const F32_ARRAY: [f32; 2] = [5.5, 5.5]; + +fn main() { + { + fn _f(x: f32, y: f32) { + let _ = x == y; //~ float_cmp + let _ = x != y; //~ float_cmp + let _ = x == 5.5; //~ float_cmp + let _ = 5.5 == x; //~ float_cmp + + let _ = x < 5.5; + let _ = x <= 5.5; + let _ = x > 5.5; + let _ = x >= 5.5; + let _ = 5.5 < x; + let _ = 5.5 <= x; + let _ = 5.5 > x; + let _ = 5.5 >= x; + + let _ = 0.0 == x; + let _ = -0.0 == x; + let _ = 1.0 / 0.0 == x; + let _ = -1.0 / 0.0 == x; + let _ = x == 0.0; + let _ = x == -0.0; + let _ = x == 1.0 / 0.0; + let _ = x == -1.0 / 0.0; + } + } + { + fn _f(x: f64, y: f64) { + let _ = x == y; //~ float_cmp + let _ = x != y; //~ float_cmp + let _ = x == 5.5; //~ float_cmp + let _ = 5.5 == x; //~ float_cmp + + let _ = x < 5.5; + let _ = x <= 5.5; + let _ = x > 5.5; + let _ = x >= 5.5; + let _ = 5.5 < x; + let _ = 5.5 <= x; + let _ = 5.5 > x; + let _ = 5.5 >= x; + + let _ = 0.0 == x; + let _ = -0.0 == x; + let _ = 1.0 / 0.0 == x; + let _ = -1.0 / 0.0 == x; + let _ = x == 0.0; + let _ = x == -0.0; + let _ = x == 1.0 / 0.0; + let _ = x == -1.0 / 0.0; + } + } + { + fn _f(x: [f32; 4], y: [f32; 4]) { + let _ = x == y; //~ float_cmp + let _ = x == [5.5; 4]; //~ float_cmp + let _ = [5.5; 4] == x; //~ float_cmp + let _ = [0.0, 0.0, 0.0, 5.5] == x; //~ float_cmp + let _ = x == [0.0, 0.0, 0.0, 5.5]; //~ float_cmp + + let _ = [0.0; 4] == x; + let _ = [-0.0; 4] == x; + let _ = [1.0 / 0.0; 4] == x; + let _ = [-1.0 / 0.0; 4] == x; + let _ = [0.0, -0.0, 1.0 / 0.0, -1.0 / 0.0] == x; + let _ = x == [0.0; 4]; + let _ = x == [-0.0; 4]; + let _ = x == [1.0 / 0.0; 4]; + let _ = x == [-1.0 / 0.0; 4]; + let _ = x == [0.0, -0.0, 1.0 / 0.0, -1.0 / 0.0]; + } + } + { + fn _f(x: [f64; 4], y: [f64; 4]) { + let _ = x == y; //~ float_cmp + let _ = x == [5.5; 4]; //~ float_cmp + let _ = [5.5; 4] == x; //~ float_cmp + let _ = [0.0, 0.0, 0.0, 5.5] == x; //~ float_cmp + let _ = x == [0.0, 0.0, 0.0, 5.5]; //~ float_cmp + + let _ = [0.0; 4] == x; + let _ = [-0.0; 4] == x; + let _ = [1.0 / 0.0; 4] == x; + let _ = [-1.0 / 0.0; 4] == x; + let _ = [0.0, -0.0, 1.0 / 0.0, -1.0 / 0.0] == x; + let _ = x == [0.0; 4]; + let _ = x == [-0.0; 4]; + let _ = x == [1.0 / 0.0; 4]; + let _ = x == [-1.0 / 0.0; 4]; + let _ = x == [0.0, -0.0, 1.0 / 0.0, -1.0 / 0.0]; + } + } + + // Reference comparisons + { + fn _f(x: &&&f32, y: &&&f32) { + let _ = x == y; //~ float_cmp + let _ = x == &&&0.0; + } + } + { + fn _f(x: &&&[f32; 2], y: &&&[f32; 2]) { + let _ = x == y; //~ float_cmp + let _ = x == &&&[0.0, -0.0]; + } + } + + // Comparisons to named constant + { + fn _f(x: f32, y: f64) { + let _ = x == f32::EPSILON; //~[named_const] float_cmp + let _ = f32::EPSILON == x; //~[named_const] float_cmp + let _ = &&x == &&core::f32::EPSILON; //~[named_const] float_cmp + let _ = &&core::f32::EPSILON == &&x; //~[named_const] float_cmp + let _ = y == f32::EPSILON as f64; //~[named_const] float_cmp + let _ = f32::EPSILON as f64 == y; //~[named_const] float_cmp + + let _ = f32::EPSILON * x == x * x; //~ float_cmp + let _ = x * x == f32::EPSILON * x; //~ float_cmp + } + } + { + fn _f(x: [f32; 2]) { + let _ = x == F32_ARRAY; //~[named_const] float_cmp + let _ = F32_ARRAY == x; //~[named_const] float_cmp + let _ = &&x == &&F32_ARRAY; //~[named_const] float_cmp + let _ = &&F32_ARRAY == &&x; //~[named_const] float_cmp + } + } + + // Constant comparisons + { + const fn f(x: f32) -> f32 { + todo!() + } + let _ = f(1.0) == f(5.0); //~[const_cmp] float_cmp + let _ = 1.0 == f(5.0); //~[const_cmp] float_cmp + let _ = f(1.0) + 1.0 != 5.0; //~[const_cmp] float_cmp + } + { + fn f(x: f32) -> f32 { + todo!() + } + let _ = f(1.0) == f(5.0); //~ float_cmp + let _ = 1.0 == f(5.0); //~ float_cmp + let _ = f(1.0) + 1.0 != 5.0; //~ float_cmp + } + + // Pointer equality + { + fn _f(x: *const f32, y: *const f32) { + let _ = x == y; + } + } + { + fn _f(x: *const [f32; 2], y: *const [f32; 2]) { + let _ = x == y; + } + } + + // `signum` + { + fn _f(x: f32, y: f32) { + let _ = x.signum() == y.signum(); + let _ = x.signum() == -y.signum(); + let _ = -x.signum() == y.signum(); + let _ = -x.signum() == -y.signum(); + } + } + { + fn _f(x: f64, y: f64) { + let _ = x.signum() == y.signum(); + let _ = x.signum() == -y.signum(); + let _ = -x.signum() == y.signum(); + let _ = -x.signum() == -y.signum(); + } + } + + // Index constant array + { + const C: [f32; 3] = [0.0, 5.5, -0.0]; + fn _f(x: f32) { + let _ = x == C[0]; + let _ = x == C[2]; + let _ = C[0] == x; + let _ = C[2] == x; + + let _ = x == C[1]; //~ float_cmp + let _ = C[1] == x; //~ float_cmp + } + } + + // `eq` functions + { + fn eq(x: f32, y: f32) -> bool { + x == y + } + + fn ne(x: f32, y: f32) -> bool { + x != y + } + + fn is_eq(x: f32, y: f32) -> bool { + x == y + } + + struct _X(f32); + impl PartialEq for _X { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } + } + + fn eq_fl(x: f32, y: f32) -> bool { + if x.is_nan() { y.is_nan() } else { x == y } + } + + fn fl_eq(x: f32, y: f32) -> bool { + if x.is_nan() { y.is_nan() } else { x == y } + } + } + + // Custom types + { + struct S; + impl PartialEq for S { + fn eq(&self, _: &f32) -> bool { + false + } + } + impl PartialEq for f32 { + fn eq(&self, _: &S) -> bool { + false + } + } + + fn _f(x: S, y: f32) { + let _ = x == y; + let _ = y == x; + } + } + + // modified operands + { + fn f1(x: f32) -> f32 { + x + 1.0 + } + + fn f2(x: f32, y: f32) -> f32 { + x + y + } + + fn _f(x: f32, y: f32) { + let _ = x == x + 1.0; //~[change_detect] float_cmp + let _ = x + 1.0 == x; //~[change_detect] float_cmp + let _ = -x == -x + 1.0; //~[change_detect] float_cmp + let _ = -x + 1.0 == -x; //~[change_detect] float_cmp + let _ = x == f1(x); //~[change_detect] float_cmp + let _ = f1(x) == x; //~[change_detect] float_cmp + let _ = x == f2(x, y); //~[change_detect] float_cmp + let _ = f2(x, y) == x; //~[change_detect] float_cmp + let _ = f1(f1(x)) == f1(x); //~[change_detect] float_cmp + let _ = f1(x) == f1(f1(x)); //~[change_detect] float_cmp + + let z = (x, y); + let _ = z.0 == z.0 + 1.0; //~[change_detect] float_cmp + let _ = z.0 + 1.0 == z.0; //~[change_detect] float_cmp + } + + fn _f2(x: &f32) { + let _ = *x + 1.0 == *x; //~[change_detect] float_cmp + let _ = *x == *x + 1.0; //~[change_detect] float_cmp + let _ = *x == f1(*x); //~[change_detect] float_cmp + let _ = f1(*x) == *x; //~[change_detect] float_cmp + } + } + { + fn _f(mut x: impl Iterator) { + let _ = x.next().unwrap() == x.next().unwrap() + 1.0; //~ float_cmp + } + } + { + use core::cell::RefCell; + + struct S(RefCell); + impl S { + fn f(&self) -> f32 { + let x = *self.0.borrow(); + *self.0.borrow_mut() *= 2.0; + x + } + } + + fn _f(x: S) { + let _ = x.f() + 1.0 == x.f(); //~ float_cmp + let _ = x.f() == x.f() + 1.0; //~ float_cmp + } + } + { + let f = |x: f32| -> f32 { x }; + let _ = f(1.0) == f(1.0) + 1.0; + + let mut x = 1.0; + let mut f = |y: f32| -> f32 { core::mem::replace(&mut x, y) }; + let _ = f(1.0) == f(1.0) + 1.0; //~ float_cmp + } + + // Self comparisons + { + fn _f(x: f32) { + let _ = x == x; + let _ = x != x; + let _ = x == -x; + let _ = -x == x; + let _ = x as f64 == x as f64; + let _ = &&x == &&x; + } + } + + // Constant to literal + { + fn _f(x: f64) { + let _ = f64::EPSILON == 2.2204460492503131E-16f64; + let _ = 2.2204460492503131E-16f64 == core::f64::EPSILON; + } + } +} diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 5cf9c0fb2710..2a8a2520141a 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -42,6 +42,9 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect enum-variant-name-threshold enum-variant-size-threshold excessive-nesting-threshold + float-cmp-allowed-constants + float-cmp-ignore-change-detection + float-cmp-ignore-constant-comparisons future-size-threshold ignore-interior-mutability large-error-threshold @@ -126,6 +129,9 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect enum-variant-name-threshold enum-variant-size-threshold excessive-nesting-threshold + float-cmp-allowed-constants + float-cmp-ignore-change-detection + float-cmp-ignore-constant-comparisons future-size-threshold ignore-interior-mutability large-error-threshold @@ -210,6 +216,9 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni enum-variant-name-threshold enum-variant-size-threshold excessive-nesting-threshold + float-cmp-allowed-constants + float-cmp-ignore-change-detection + float-cmp-ignore-constant-comparisons future-size-threshold ignore-interior-mutability large-error-threshold diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs index d3c34fb37167..389cc72e3720 100644 --- a/tests/ui/deprecated.rs +++ b/tests/ui/deprecated.rs @@ -20,5 +20,6 @@ #![warn(clippy::wrong_pub_self_convention)] #![warn(clippy::maybe_misused_cfg)] #![warn(clippy::mismatched_target_os)] +#![warn(clippy::float_cmp_const)] fn main() {} diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index 49b90c70c06e..dd436744ee9e 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -109,5 +109,11 @@ error: lint `clippy::mismatched_target_os` has been removed: this lint has been LL | #![warn(clippy::mismatched_target_os)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 18 previous errors +error: lint `clippy::float_cmp_const` has been removed: `float_cmp` handles this via config options + --> tests/ui/deprecated.rs:23:9 + | +LL | #![warn(clippy::float_cmp_const)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 19 previous errors diff --git a/tests/ui/float_cmp.rs b/tests/ui/float_cmp.rs deleted file mode 100644 index 78dd2c6c01c3..000000000000 --- a/tests/ui/float_cmp.rs +++ /dev/null @@ -1,136 +0,0 @@ -// FIXME(f16_f128): const casting is not yet supported for these types. Add when available. - -#![warn(clippy::float_cmp)] -#![allow( - unused, - clippy::no_effect, - clippy::op_ref, - clippy::unnecessary_operation, - clippy::cast_lossless -)] -//@no-rustfix -use std::ops::Add; - -const ZERO: f32 = 0.0; -const ONE: f32 = ZERO + 1.0; - -fn twice(x: T) -> T -where - T: Add + Copy, -{ - x + x -} - -fn eq_fl(x: f32, y: f32) -> bool { - if x.is_nan() { y.is_nan() } else { x == y } // no error, inside "eq" fn -} - -fn fl_eq(x: f32, y: f32) -> bool { - if x.is_nan() { y.is_nan() } else { x == y } // no error, inside "eq" fn -} - -struct X { - val: f32, -} - -impl PartialEq for X { - fn eq(&self, o: &X) -> bool { - if self.val.is_nan() { - o.val.is_nan() - } else { - self.val == o.val // no error, inside "eq" fn - } - } -} - -impl PartialEq for X { - fn eq(&self, o: &f32) -> bool { - if self.val.is_nan() { - o.is_nan() - } else { - self.val == *o // no error, inside "eq" fn - } - } -} - -fn main() { - ZERO == 0f32; //no error, comparison with zero is ok - 1.0f32 != f32::INFINITY; // also comparison with infinity - 1.0f32 != f32::NEG_INFINITY; // and negative infinity - ZERO == 0.0; //no error, comparison with zero is ok - ZERO + ZERO != 1.0; //no error, comparison with zero is ok - - let x = X { val: 1.0 }; - x == 1.0; // no error, custom type that implement PartialOrder for float is not checked - - ONE == 1f32; - ONE == 1.0 + 0.0; - ONE + ONE == ZERO + ONE + ONE; - ONE != 2.0; - ONE != 0.0; // no error, comparison with zero is ok - twice(ONE) != ONE; - ONE as f64 != 2.0; - //~^ ERROR: strict comparison of `f32` or `f64` - ONE as f64 != 0.0; // no error, comparison with zero is ok - - let x: f64 = 1.0; - - x == 1.0; - //~^ ERROR: strict comparison of `f32` or `f64` - x != 0f64; // no error, comparison with zero is ok - - twice(x) != twice(ONE as f64); - //~^ ERROR: strict comparison of `f32` or `f64` - - x < 0.0; // no errors, lower or greater comparisons need no fuzzyness - x > 0.0; - x <= 0.0; - x >= 0.0; - - let xs: [f32; 1] = [0.0]; - let a: *const f32 = xs.as_ptr(); - let b: *const f32 = xs.as_ptr(); - - assert_eq!(a, b); // no errors - - const ZERO_ARRAY: [f32; 2] = [0.0, 0.0]; - const NON_ZERO_ARRAY: [f32; 2] = [0.0, 0.1]; - - let i = 0; - let j = 1; - - ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; // ok, because lhs is zero regardless of i - NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; - //~^ ERROR: strict comparison of `f32` or `f64` - - let a1: [f32; 1] = [0.0]; - let a2: [f32; 1] = [1.1]; - - a1 == a2; - //~^ ERROR: strict comparison of `f32` or `f64` arrays - a1[0] == a2[0]; - //~^ ERROR: strict comparison of `f32` or `f64` - - // no errors - comparing signums is ok - let x32 = 3.21f32; - 1.23f32.signum() == x32.signum(); - 1.23f32.signum() == -(x32.signum()); - 1.23f32.signum() == 3.21f32.signum(); - - 1.23f32.signum() != x32.signum(); - 1.23f32.signum() != -(x32.signum()); - 1.23f32.signum() != 3.21f32.signum(); - - let x64 = 3.21f64; - 1.23f64.signum() == x64.signum(); - 1.23f64.signum() == -(x64.signum()); - 1.23f64.signum() == 3.21f64.signum(); - - 1.23f64.signum() != x64.signum(); - 1.23f64.signum() != -(x64.signum()); - 1.23f64.signum() != 3.21f64.signum(); - - // the comparison should also look through references - &0.0 == &ZERO; - &&&&0.0 == &&&&ZERO; -} diff --git a/tests/ui/float_cmp.stderr b/tests/ui/float_cmp.stderr deleted file mode 100644 index d10da8a99a9d..000000000000 --- a/tests/ui/float_cmp.stderr +++ /dev/null @@ -1,41 +0,0 @@ -error: strict comparison of `f32` or `f64` - --> tests/ui/float_cmp.rs:72:5 - | -LL | ONE as f64 != 2.0; - | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin` - | - = note: `-D clippy::float-cmp` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::float_cmp)]` - -error: strict comparison of `f32` or `f64` - --> tests/ui/float_cmp.rs:78:5 - | -LL | x == 1.0; - | ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin` - -error: strict comparison of `f32` or `f64` - --> tests/ui/float_cmp.rs:82:5 - | -LL | twice(x) != twice(ONE as f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin` - -error: strict comparison of `f32` or `f64` - --> tests/ui/float_cmp.rs:103:5 - | -LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin` - -error: strict comparison of `f32` or `f64` arrays - --> tests/ui/float_cmp.rs:109:5 - | -LL | a1 == a2; - | ^^^^^^^^ - -error: strict comparison of `f32` or `f64` - --> tests/ui/float_cmp.rs:111:5 - | -LL | a1[0] == a2[0]; - | ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin` - -error: aborting due to 6 previous errors - diff --git a/tests/ui/float_cmp_const.rs b/tests/ui/float_cmp_const.rs deleted file mode 100644 index 081805564373..000000000000 --- a/tests/ui/float_cmp_const.rs +++ /dev/null @@ -1,66 +0,0 @@ -// does not test any rustfixable lints -//@no-rustfix -#![warn(clippy::float_cmp_const)] -#![allow(clippy::float_cmp)] -#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)] - -const ONE: f32 = 1.0; -const TWO: f32 = 2.0; - -fn eq_one(x: f32) -> bool { - if x.is_nan() { false } else { x == ONE } // no error, inside "eq" fn -} - -fn main() { - // has errors - 1f32 == ONE; - //~^ ERROR: strict comparison of `f32` or `f64` constant - TWO == ONE; - //~^ ERROR: strict comparison of `f32` or `f64` constant - TWO != ONE; - //~^ ERROR: strict comparison of `f32` or `f64` constant - ONE + ONE == TWO; - //~^ ERROR: strict comparison of `f32` or `f64` constant - let x = 1; - x as f32 == ONE; - //~^ ERROR: strict comparison of `f32` or `f64` constant - - let v = 0.9; - v == ONE; - //~^ ERROR: strict comparison of `f32` or `f64` constant - v != ONE; - //~^ ERROR: strict comparison of `f32` or `f64` constant - - // no errors, lower than or greater than comparisons - v < ONE; - v > ONE; - v <= ONE; - v >= ONE; - - // no errors, zero and infinity values - ONE != 0f32; - TWO == 0f32; - ONE != f32::INFINITY; - ONE == f32::NEG_INFINITY; - - // no errors, but will warn clippy::float_cmp if '#![allow(float_cmp)]' above is removed - let w = 1.1; - v == w; - v != w; - v == 1.0; - v != 1.0; - - const ZERO_ARRAY: [f32; 3] = [0.0, 0.0, 0.0]; - const ZERO_INF_ARRAY: [f32; 3] = [0.0, f32::INFINITY, f32::NEG_INFINITY]; - const NON_ZERO_ARRAY: [f32; 3] = [0.0, 0.1, 0.2]; - const NON_ZERO_ARRAY2: [f32; 3] = [0.2, 0.1, 0.0]; - - // no errors, zero and infinity values - NON_ZERO_ARRAY[0] == NON_ZERO_ARRAY2[1]; // lhs is 0.0 - ZERO_ARRAY == NON_ZERO_ARRAY; // lhs is all zeros - ZERO_INF_ARRAY == NON_ZERO_ARRAY; // lhs is all zeros or infinities - - // has errors - NON_ZERO_ARRAY == NON_ZERO_ARRAY2; - //~^ ERROR: strict comparison of `f32` or `f64` constant arrays -} diff --git a/tests/ui/float_cmp_const.stderr b/tests/ui/float_cmp_const.stderr deleted file mode 100644 index 4f88746e958e..000000000000 --- a/tests/ui/float_cmp_const.stderr +++ /dev/null @@ -1,53 +0,0 @@ -error: strict comparison of `f32` or `f64` constant - --> tests/ui/float_cmp_const.rs:16:5 - | -LL | 1f32 == ONE; - | ^^^^^^^^^^^ help: consider comparing them within some margin of error: `(1f32 - ONE).abs() < error_margin` - | - = note: `-D clippy::float-cmp-const` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::float_cmp_const)]` - -error: strict comparison of `f32` or `f64` constant - --> tests/ui/float_cmp_const.rs:18:5 - | -LL | TWO == ONE; - | ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() < error_margin` - -error: strict comparison of `f32` or `f64` constant - --> tests/ui/float_cmp_const.rs:20:5 - | -LL | TWO != ONE; - | ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() > error_margin` - -error: strict comparison of `f32` or `f64` constant - --> tests/ui/float_cmp_const.rs:22:5 - | -LL | ONE + ONE == TWO; - | ^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE + ONE - TWO).abs() < error_margin` - -error: strict comparison of `f32` or `f64` constant - --> tests/ui/float_cmp_const.rs:25:5 - | -LL | x as f32 == ONE; - | ^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x as f32 - ONE).abs() < error_margin` - -error: strict comparison of `f32` or `f64` constant - --> tests/ui/float_cmp_const.rs:29:5 - | -LL | v == ONE; - | ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() < error_margin` - -error: strict comparison of `f32` or `f64` constant - --> tests/ui/float_cmp_const.rs:31:5 - | -LL | v != ONE; - | ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() > error_margin` - -error: strict comparison of `f32` or `f64` constant arrays - --> tests/ui/float_cmp_const.rs:64:5 - | -LL | NON_ZERO_ARRAY == NON_ZERO_ARRAY2; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 8 previous errors -