1 use rustc::declare_lint_pass;
3 use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
4 use rustc_errors::Applicability;
5 use rustc_session::declare_tool_lint;
10 /// **What it does:** Checks for expressions of the form `a * b + c`
11 /// or `c + a * b` where `a`, `b`, `c` are floats and suggests using
12 /// `a.mul_add(b, c)` instead.
14 /// **Why is this bad?** Calculating `a * b + c` may lead to slight
15 /// numerical inaccuracies as `a * b` is rounded before being added to
16 /// `c`. Depending on the target architecture, `mul_add()` may be more
19 /// **Known problems:** This lint can emit semantic incorrect suggestions.
20 /// For example, for `a * b * c + d` the suggestion `a * b.mul_add(c, d)`
21 /// is emitted, which is equivalent to `a * (b * c + d)`. (#4735)
29 /// let foo = (a * b) + c;
38 /// let foo = a.mul_add(b, c);
42 "Using `a.mul_add(b, c)` for floating points has higher numerical precision than `a * b + c`"
45 declare_lint_pass!(MulAddCheck => [MANUAL_MUL_ADD]);
47 fn is_float<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr) -> bool {
48 cx.tables.expr_ty(expr).is_floating_point()
51 // Checks whether expression is multiplication of two floats
52 fn is_float_mult_expr<'a, 'tcx, 'b>(cx: &LateContext<'a, 'tcx>, expr: &'b Expr) -> Option<(&'b Expr, &'b Expr)> {
53 if let ExprKind::Binary(op, lhs, rhs) = &expr.kind {
54 if let BinOpKind::Mul = op.node {
55 if is_float(cx, &lhs) && is_float(cx, &rhs) {
56 return Some((&lhs, &rhs));
64 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MulAddCheck {
65 fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
66 if let ExprKind::Binary(op, lhs, rhs) = &expr.kind {
67 if let BinOpKind::Add = op.node {
68 //Converts mult_lhs * mult_rhs + rhs to mult_lhs.mult_add(mult_rhs, rhs)
69 if let Some((mult_lhs, mult_rhs)) = is_float_mult_expr(cx, lhs) {
70 if is_float(cx, rhs) {
75 "consider using mul_add() for better numerical precision",
79 snippet(cx, mult_lhs.span, "_"),
80 snippet(cx, mult_rhs.span, "_"),
81 snippet(cx, rhs.span, "_"),
83 Applicability::MaybeIncorrect,
87 //Converts lhs + mult_lhs * mult_rhs to mult_lhs.mult_add(mult_rhs, lhs)
88 if let Some((mult_lhs, mult_rhs)) = is_float_mult_expr(cx, rhs) {
89 if is_float(cx, lhs) {
94 "consider using mul_add() for better numerical precision",
98 snippet(cx, mult_lhs.span, "_"),
99 snippet(cx, mult_rhs.span, "_"),
100 snippet(cx, lhs.span, "_"),
102 Applicability::MaybeIncorrect,