1 use clippy_utils
::consts
::{constant, Constant}
;
2 use clippy_utils
::diagnostics
::span_lint_and_then
;
3 use clippy_utils
::get_item_name
;
4 use clippy_utils
::sugg
::Sugg
;
5 use if_chain
::if_chain
;
6 use rustc_errors
::Applicability
;
7 use rustc_hir
::{BinOpKind, Expr, ExprKind, UnOp}
;
8 use rustc_lint
::LateContext
;
11 use super::{FLOAT_CMP, FLOAT_CMP_CONST}
;
13 pub(crate) fn check
<'tcx
>(
14 cx
: &LateContext
<'tcx
>,
18 right
: &'tcx Expr
<'_
>,
20 if (op
== BinOpKind
::Eq
|| op
== BinOpKind
::Ne
) && (is_float(cx
, left
) || is_float(cx
, right
)) {
21 if is_allowed(cx
, left
) || is_allowed(cx
, right
) {
25 // Allow comparing the results of signum()
26 if is_signum(cx
, left
) && is_signum(cx
, right
) {
30 if let Some(name
) = get_item_name(cx
, expr
) {
31 let name
= name
.as_str();
32 if name
== "eq" || name
== "ne" || name
== "is_nan" || name
.starts_with("eq_") || name
.ends_with("_eq") {
36 let is_comparing_arrays
= is_array(cx
, left
) || is_array(cx
, right
);
37 let (lint
, msg
) = get_lint_and_message(
38 is_named_constant(cx
, left
) || is_named_constant(cx
, right
),
41 span_lint_and_then(cx
, lint
, expr
.span
, msg
, |diag
| {
42 let lhs
= Sugg
::hir(cx
, left
, "..");
43 let rhs
= Sugg
::hir(cx
, right
, "..");
45 if !is_comparing_arrays
{
48 "consider comparing them within some margin of error",
50 "({}).abs() {} error_margin",
52 if op
== BinOpKind
::Eq { '<' }
else { '>' }
54 Applicability
::HasPlaceholders
, // snippet
57 diag
.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`");
62 fn get_lint_and_message(
63 is_comparing_constants
: bool
,
64 is_comparing_arrays
: bool
,
65 ) -> (&'
static rustc_lint
::Lint
, &'
static str) {
66 if is_comparing_constants
{
69 if is_comparing_arrays
{
70 "strict comparison of `f32` or `f64` constant arrays"
72 "strict comparison of `f32` or `f64` constant"
78 if is_comparing_arrays
{
79 "strict comparison of `f32` or `f64` arrays"
81 "strict comparison of `f32` or `f64`"
87 fn is_named_constant
<'tcx
>(cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'_
>) -> bool
{
88 if let Some((_
, res
)) = constant(cx
, cx
.typeck_results(), expr
) {
95 fn is_allowed
<'tcx
>(cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'_
>) -> bool
{
96 match constant(cx
, cx
.typeck_results(), expr
) {
97 Some((Constant
::F32(f
), _
)) => f
== 0.0 || f
.is_infinite(),
98 Some((Constant
::F64(f
), _
)) => f
== 0.0 || f
.is_infinite(),
99 Some((Constant
::Vec(vec
), _
)) => vec
.iter().all(|f
| match f
{
100 Constant
::F32(f
) => *f
== 0.0 || (*f
).is_infinite(),
101 Constant
::F64(f
) => *f
== 0.0 || (*f
).is_infinite(),
108 // Return true if `expr` is the result of `signum()` invoked on a float value.
109 fn is_signum(cx
: &LateContext
<'_
>, expr
: &Expr
<'_
>) -> bool
{
110 // The negation of a signum is still a signum
111 if let ExprKind
::Unary(UnOp
::Neg
, child_expr
) = expr
.kind
{
112 return is_signum(cx
, child_expr
);
116 if let ExprKind
::MethodCall(method_name
, self_arg
, ..) = expr
.kind
;
117 if sym
!(signum
) == method_name
.ident
.name
;
118 // Check that the receiver of the signum() is a float (expressions[0] is the receiver of
121 return is_float(cx
, self_arg
);
127 fn is_float(cx
: &LateContext
<'_
>, expr
: &Expr
<'_
>) -> bool
{
128 let value
= &cx
.typeck_results().expr_ty(expr
).peel_refs().kind();
130 if let ty
::Array(arr_ty
, _
) = value
{
131 return matches
!(arr_ty
.kind(), ty
::Float(_
));
134 matches
!(value
, ty
::Float(_
))
137 fn is_array(cx
: &LateContext
<'_
>, expr
: &Expr
<'_
>) -> bool
{
138 matches
!(&cx
.typeck_results().expr_ty(expr
).peel_refs().kind(), ty
::Array(_
, _
))