1 use clippy_utils
::diagnostics
::span_lint_and_then
;
2 use clippy_utils
::{match_def_path, paths, sugg}
;
3 use if_chain
::if_chain
;
4 use rustc_ast
::util
::parser
::AssocOp
;
5 use rustc_errors
::Applicability
;
6 use rustc_hir
::def
::{DefKind, Res}
;
7 use rustc_hir
::{BinOpKind, Expr, ExprKind}
;
8 use rustc_lint
::{LateContext, LateLintPass}
;
10 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
11 use rustc_span
::source_map
::Spanned
;
13 declare_clippy_lint
! {
15 /// Checks for statements of the form `(a - b) < f32::EPSILON` or
16 /// `(a - b) < f64::EPSILON`. Notes the missing `.abs()`.
18 /// ### Why is this bad?
19 /// The code without `.abs()` is more likely to have a bug.
21 /// ### Known problems
22 /// If the user can ensure that b is larger than a, the `.abs()` is
23 /// technically unnecessary. However, it will make the code more robust and doesn't have any
24 /// large performance implications. If the abs call was deliberately left out for performance
25 /// reasons, it is probably better to state this explicitly in the code, which then can be done
30 /// pub fn is_roughly_equal(a: f32, b: f32) -> bool {
31 /// (a - b) < f32::EPSILON
36 /// pub fn is_roughly_equal(a: f32, b: f32) -> bool {
37 /// (a - b).abs() < f32::EPSILON
40 #[clippy::version = "1.48.0"]
41 pub FLOAT_EQUALITY_WITHOUT_ABS
,
43 "float equality check without `.abs()`"
46 declare_lint_pass
!(FloatEqualityWithoutAbs
=> [FLOAT_EQUALITY_WITHOUT_ABS
]);
48 impl<'tcx
> LateLintPass
<'tcx
> for FloatEqualityWithoutAbs
{
49 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'_
>) {
53 // check if expr is a binary expression with a lt or gt operator
54 if let ExprKind
::Binary(op
, left
, right
) = expr
.kind
{
72 // left hand side is a subtraction
73 if let ExprKind
::Binary(
82 // right hand side matches either f32::EPSILON or f64::EPSILON
83 if let ExprKind
::Path(ref epsilon_path
) = rhs
.kind
;
84 if let Res
::Def(DefKind
::AssocConst
, def_id
) = cx
.qpath_res(epsilon_path
, rhs
.hir_id
);
85 if match_def_path(cx
, def_id
, &paths
::F32_EPSILON
) || match_def_path(cx
, def_id
, &paths
::F64_EPSILON
);
87 // values of the subtractions on the left hand side are of the type float
88 let t_val_l
= cx
.typeck_results().expr_ty(val_l
);
89 let t_val_r
= cx
.typeck_results().expr_ty(val_r
);
90 if let ty
::Float(_
) = t_val_l
.kind();
91 if let ty
::Float(_
) = t_val_r
.kind();
94 let sug_l
= sugg
::Sugg
::hir(cx
, val_l
, "..");
95 let sug_r
= sugg
::Sugg
::hir(cx
, val_r
, "..");
96 // format the suggestion
97 let suggestion
= format
!("{}.abs()", sugg
::make_assoc(AssocOp
::Subtract
, &sug_l
, &sug_r
).maybe_par());
101 FLOAT_EQUALITY_WITHOUT_ABS
,
103 "float equality check without `.abs()`",
105 diag
.span_suggestion(
109 Applicability
::MaybeIncorrect
,