]>
Commit | Line | Data |
---|---|---|
cdc7bbd5 XL |
1 | use clippy_utils::diagnostics::span_lint_and_then; |
2 | use clippy_utils::{match_def_path, paths, sugg}; | |
f20569fa XL |
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}; | |
9 | use rustc_middle::ty; | |
10 | use rustc_session::{declare_lint_pass, declare_tool_lint}; | |
11 | use rustc_span::source_map::Spanned; | |
12 | ||
13 | declare_clippy_lint! { | |
14 | /// **What it does:** Checks for statements of the form `(a - b) < f32::EPSILON` or | |
15 | /// `(a - b) < f64::EPSILON`. Notes the missing `.abs()`. | |
16 | /// | |
17 | /// **Why is this bad?** The code without `.abs()` is more likely to have a bug. | |
18 | /// | |
19 | /// **Known problems:** If the user can ensure that b is larger than a, the `.abs()` is | |
20 | /// technically unneccessary. However, it will make the code more robust and doesn't have any | |
21 | /// large performance implications. If the abs call was deliberately left out for performance | |
22 | /// reasons, it is probably better to state this explicitly in the code, which then can be done | |
23 | /// with an allow. | |
24 | /// | |
25 | /// **Example:** | |
26 | /// | |
27 | /// ```rust | |
28 | /// pub fn is_roughly_equal(a: f32, b: f32) -> bool { | |
29 | /// (a - b) < f32::EPSILON | |
30 | /// } | |
31 | /// ``` | |
32 | /// Use instead: | |
33 | /// ```rust | |
34 | /// pub fn is_roughly_equal(a: f32, b: f32) -> bool { | |
35 | /// (a - b).abs() < f32::EPSILON | |
36 | /// } | |
37 | /// ``` | |
38 | pub FLOAT_EQUALITY_WITHOUT_ABS, | |
136023e0 | 39 | suspicious, |
f20569fa XL |
40 | "float equality check without `.abs()`" |
41 | } | |
42 | ||
43 | declare_lint_pass!(FloatEqualityWithoutAbs => [FLOAT_EQUALITY_WITHOUT_ABS]); | |
44 | ||
45 | impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs { | |
46 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { | |
47 | let lhs; | |
48 | let rhs; | |
49 | ||
50 | // check if expr is a binary expression with a lt or gt operator | |
cdc7bbd5 | 51 | if let ExprKind::Binary(op, left, right) = expr.kind { |
f20569fa XL |
52 | match op.node { |
53 | BinOpKind::Lt => { | |
54 | lhs = left; | |
55 | rhs = right; | |
56 | }, | |
57 | BinOpKind::Gt => { | |
58 | lhs = right; | |
59 | rhs = left; | |
60 | }, | |
61 | _ => return, | |
62 | }; | |
63 | } else { | |
64 | return; | |
65 | } | |
66 | ||
67 | if_chain! { | |
68 | ||
69 | // left hand side is a substraction | |
70 | if let ExprKind::Binary( | |
71 | Spanned { | |
72 | node: BinOpKind::Sub, | |
73 | .. | |
74 | }, | |
75 | val_l, | |
76 | val_r, | |
77 | ) = lhs.kind; | |
78 | ||
79 | // right hand side matches either f32::EPSILON or f64::EPSILON | |
80 | if let ExprKind::Path(ref epsilon_path) = rhs.kind; | |
81 | if let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id); | |
82 | if match_def_path(cx, def_id, &paths::F32_EPSILON) || match_def_path(cx, def_id, &paths::F64_EPSILON); | |
83 | ||
84 | // values of the substractions on the left hand side are of the type float | |
85 | let t_val_l = cx.typeck_results().expr_ty(val_l); | |
86 | let t_val_r = cx.typeck_results().expr_ty(val_r); | |
87 | if let ty::Float(_) = t_val_l.kind(); | |
88 | if let ty::Float(_) = t_val_r.kind(); | |
89 | ||
90 | then { | |
cdc7bbd5 XL |
91 | let sug_l = sugg::Sugg::hir(cx, val_l, ".."); |
92 | let sug_r = sugg::Sugg::hir(cx, val_r, ".."); | |
f20569fa XL |
93 | // format the suggestion |
94 | let suggestion = format!("{}.abs()", sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par()); | |
95 | // spans the lint | |
96 | span_lint_and_then( | |
97 | cx, | |
98 | FLOAT_EQUALITY_WITHOUT_ABS, | |
99 | expr.span, | |
100 | "float equality check without `.abs()`", | |
101 | | diag | { | |
102 | diag.span_suggestion( | |
103 | lhs.span, | |
104 | "add `.abs()`", | |
105 | suggestion, | |
106 | Applicability::MaybeIncorrect, | |
107 | ); | |
108 | } | |
109 | ); | |
110 | } | |
111 | } | |
112 | } | |
113 | } |