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