2019-12-03 23:16:03 +00:00
|
|
|
use rustc::declare_lint_pass;
|
2019-09-28 18:40:10 +00:00
|
|
|
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
|
|
|
use rustc_errors::Applicability;
|
2020-01-06 16:39:50 +00:00
|
|
|
use rustc_hir::*;
|
2019-12-03 23:16:03 +00:00
|
|
|
use rustc_session::declare_tool_lint;
|
2019-09-28 18:40:10 +00:00
|
|
|
|
|
|
|
use crate::utils::*;
|
|
|
|
|
|
|
|
declare_clippy_lint! {
|
|
|
|
/// **What it does:** Checks for expressions of the form `a * b + c`
|
|
|
|
/// or `c + a * b` where `a`, `b`, `c` are floats and suggests using
|
|
|
|
/// `a.mul_add(b, c)` instead.
|
|
|
|
///
|
|
|
|
/// **Why is this bad?** Calculating `a * b + c` may lead to slight
|
|
|
|
/// numerical inaccuracies as `a * b` is rounded before being added to
|
|
|
|
/// `c`. Depending on the target architecture, `mul_add()` may be more
|
|
|
|
/// performant.
|
|
|
|
///
|
2019-10-26 17:56:36 +00:00
|
|
|
/// **Known problems:** This lint can emit semantic incorrect suggestions.
|
|
|
|
/// For example, for `a * b * c + d` the suggestion `a * b.mul_add(c, d)`
|
|
|
|
/// is emitted, which is equivalent to `a * (b * c + d)`. (#4735)
|
2019-09-28 18:40:10 +00:00
|
|
|
///
|
|
|
|
/// **Example:**
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// # let a = 0_f32;
|
|
|
|
/// # let b = 0_f32;
|
|
|
|
/// # let c = 0_f32;
|
|
|
|
/// let foo = (a * b) + c;
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// can be written as
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// # let a = 0_f32;
|
|
|
|
/// # let b = 0_f32;
|
|
|
|
/// # let c = 0_f32;
|
|
|
|
/// let foo = a.mul_add(b, c);
|
|
|
|
/// ```
|
|
|
|
pub MANUAL_MUL_ADD,
|
2019-10-26 17:56:36 +00:00
|
|
|
nursery,
|
2019-09-28 18:40:10 +00:00
|
|
|
"Using `a.mul_add(b, c)` for floating points has higher numerical precision than `a * b + c`"
|
|
|
|
}
|
|
|
|
|
|
|
|
declare_lint_pass!(MulAddCheck => [MANUAL_MUL_ADD]);
|
|
|
|
|
2019-12-27 07:12:26 +00:00
|
|
|
fn is_float<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr<'_>) -> bool {
|
2019-09-28 18:40:10 +00:00
|
|
|
cx.tables.expr_ty(expr).is_floating_point()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Checks whether expression is multiplication of two floats
|
2019-12-27 07:12:26 +00:00
|
|
|
fn is_float_mult_expr<'a, 'tcx, 'b>(
|
|
|
|
cx: &LateContext<'a, 'tcx>,
|
|
|
|
expr: &'b Expr<'b>,
|
|
|
|
) -> Option<(&'b Expr<'b>, &'b Expr<'b>)> {
|
2019-09-28 18:40:10 +00:00
|
|
|
if let ExprKind::Binary(op, lhs, rhs) = &expr.kind {
|
|
|
|
if let BinOpKind::Mul = op.node {
|
|
|
|
if is_float(cx, &lhs) && is_float(cx, &rhs) {
|
|
|
|
return Some((&lhs, &rhs));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MulAddCheck {
|
2019-12-27 07:12:26 +00:00
|
|
|
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
|
2019-09-28 18:40:10 +00:00
|
|
|
if let ExprKind::Binary(op, lhs, rhs) = &expr.kind {
|
|
|
|
if let BinOpKind::Add = op.node {
|
|
|
|
//Converts mult_lhs * mult_rhs + rhs to mult_lhs.mult_add(mult_rhs, rhs)
|
|
|
|
if let Some((mult_lhs, mult_rhs)) = is_float_mult_expr(cx, lhs) {
|
|
|
|
if is_float(cx, rhs) {
|
|
|
|
span_lint_and_sugg(
|
|
|
|
cx,
|
|
|
|
MANUAL_MUL_ADD,
|
|
|
|
expr.span,
|
|
|
|
"consider using mul_add() for better numerical precision",
|
|
|
|
"try",
|
|
|
|
format!(
|
|
|
|
"{}.mul_add({}, {})",
|
|
|
|
snippet(cx, mult_lhs.span, "_"),
|
|
|
|
snippet(cx, mult_rhs.span, "_"),
|
|
|
|
snippet(cx, rhs.span, "_"),
|
|
|
|
),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//Converts lhs + mult_lhs * mult_rhs to mult_lhs.mult_add(mult_rhs, lhs)
|
|
|
|
if let Some((mult_lhs, mult_rhs)) = is_float_mult_expr(cx, rhs) {
|
|
|
|
if is_float(cx, lhs) {
|
|
|
|
span_lint_and_sugg(
|
|
|
|
cx,
|
|
|
|
MANUAL_MUL_ADD,
|
|
|
|
expr.span,
|
|
|
|
"consider using mul_add() for better numerical precision",
|
|
|
|
"try",
|
|
|
|
format!(
|
|
|
|
"{}.mul_add({}, {})",
|
|
|
|
snippet(cx, mult_lhs.span, "_"),
|
|
|
|
snippet(cx, mult_rhs.span, "_"),
|
|
|
|
snippet(cx, lhs.span, "_"),
|
|
|
|
),
|
|
|
|
Applicability::MaybeIncorrect,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|