blob: a61a2a82c0237806d38e2543a2719c2ce8fb0291 [file] [log] [blame] [edit]
use clippy_utils::consts::ConstEvalCtxt;
use clippy_utils::consts::Constant::Int;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::sugg::Sugg;
use clippy_utils::{get_parent_expr, sym};
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment};
use rustc_lint::LateContext;
use rustc_span::source_map::Spanned;
use super::SUBOPTIMAL_FLOPS;
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0])
&& value == Int(2)
&& let Some(parent) = get_parent_expr(cx, expr)
{
if let Some(grandparent) = get_parent_expr(cx, parent)
&& let ExprKind::MethodCall(PathSegment { ident: method, .. }, receiver, ..) = grandparent.kind
&& method.name == sym::sqrt
// we don't care about the applicability as this is an early-return condition
&& super::hypot::detect(cx, receiver, &mut Applicability::Unspecified).is_some()
{
return;
}
if let ExprKind::Binary(
Spanned {
node: op @ (BinOpKind::Add | BinOpKind::Sub),
..
},
lhs,
rhs,
) = parent.kind
{
span_lint_and_then(
cx,
SUBOPTIMAL_FLOPS,
parent.span,
"multiply and add expressions can be calculated more efficiently and accurately",
|diag| {
let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs };
// Negate expr if original code has subtraction and expr is on the right side
let maybe_neg_sugg = |expr, hir_id, app: &mut _| {
let sugg = Sugg::hir_with_applicability(cx, expr, "_", app);
if matches!(op, BinOpKind::Sub) && hir_id == rhs.hir_id {
-sugg
} else {
sugg
}
};
let mut app = Applicability::MachineApplicable;
diag.span_suggestion(
parent.span,
"consider using",
format!(
"{}.mul_add({}, {})",
Sugg::hir_with_applicability(cx, receiver, "_", &mut app).maybe_paren(),
maybe_neg_sugg(receiver, expr.hir_id, &mut app),
maybe_neg_sugg(other_addend, other_addend.hir_id, &mut app),
),
app,
);
},
);
}
}
}