+ span_lint_and_sugg(
+ cx,
+ SUBOPTIMAL_FLOPS,
+ expr.span,
+ "multiply and add expressions can be calculated more efficiently and accurately",
+ "consider using",
+ format!(
+ "{}.mul_add({}, {})",
+ prepare_receiver_sugg(cx, recv),
+ Sugg::hir(cx, arg1, ".."),
+ Sugg::hir(cx, arg2, ".."),
+ ),
+ Applicability::MachineApplicable,
+ );
+ }
+}
+
+/// Returns true iff expr is an expression which tests whether or not
+/// test is positive or an expression which tests whether or not test
+/// is nonnegative.
+/// Used for check-custom-abs function below
+fn is_testing_positive(cx: &LateContext<'_, '_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
+ if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
+ match op {
+ BinOpKind::Gt | BinOpKind::Ge => is_zero(right) && are_exprs_equal(cx, left, test),
+ BinOpKind::Lt | BinOpKind::Le => is_zero(left) && are_exprs_equal(cx, right, test),
+ _ => false,
+ }
+ } else {
+ false
+ }
+}
+
+fn is_testing_negative(cx: &LateContext<'_, '_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
+ if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
+ match op {
+ BinOpKind::Gt | BinOpKind::Ge => is_zero(left) && are_exprs_equal(cx, right, test),
+ BinOpKind::Lt | BinOpKind::Le => is_zero(right) && are_exprs_equal(cx, left, test),
+ _ => false,
+ }
+ } else {
+ false
+ }
+}
+
+fn are_exprs_equal(cx: &LateContext<'_, '_>, expr1: &Expr<'_>, expr2: &Expr<'_>) -> bool {
+ SpanlessEq::new(cx).ignore_fn().eq_expr(expr1, expr2)
+}
+
+/// Returns true iff expr is some zero literal
+fn is_zero(expr: &Expr<'_>) -> bool {
+ if let ExprKind::Lit(Lit { node: lit, .. }) = &expr.kind {
+ match lit {
+ LitKind::Int(0, _) => true,
+ LitKind::Float(symb, LitFloatType::Unsuffixed)
+ | LitKind::Float(symb, LitFloatType::Suffixed(FloatTy::F64)) => {
+ symb.as_str().parse::<f64>().unwrap() == 0.0
+ },
+ LitKind::Float(symb, LitFloatType::Suffixed(FloatTy::F32)) => symb.as_str().parse::<f32>().unwrap() == 0.0,
+ _ => false,
+ }
+ } else {
+ false
+ }
+}
+
+fn check_custom_abs(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
+ if let Some((cond, body, Some(else_body))) = higher::if_block(&expr) {
+ if let ExprKind::Block(
+ Block {
+ stmts: [],
+ expr:
+ Some(Expr {
+ kind: ExprKind::Unary(UnOp::UnNeg, else_expr),
+ ..
+ }),
+ ..
+ },
+ _,
+ ) = else_body.kind
+ {
+ if let ExprKind::Block(
+ Block {
+ stmts: [],
+ expr: Some(body),
+ ..
+ },
+ _,
+ ) = &body.kind
+ {
+ if are_exprs_equal(cx, else_expr, body) {
+ if is_testing_positive(cx, cond, body) {
+ span_lint_and_sugg(
+ cx,
+ SUBOPTIMAL_FLOPS,
+ expr.span,
+ "This looks like you've implemented your own absolute value function",
+ "try",
+ format!("{}.abs()", Sugg::hir(cx, body, "..")),
+ Applicability::MachineApplicable,
+ );
+ } else if is_testing_negative(cx, cond, body) {
+ span_lint_and_sugg(
+ cx,
+ SUBOPTIMAL_FLOPS,
+ expr.span,
+ "This looks like you've implemented your own negative absolute value function",
+ "try",
+ format!("-{}.abs()", Sugg::hir(cx, body, "..")),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }
+ }
+ if let ExprKind::Block(
+ Block {
+ stmts: [],
+ expr:
+ Some(Expr {
+ kind: ExprKind::Unary(UnOp::UnNeg, else_expr),
+ ..
+ }),
+ ..
+ },
+ _,
+ ) = &body.kind
+ {
+ if let ExprKind::Block(
+ Block {
+ stmts: [],
+ expr: Some(body),
+ ..
+ },
+ _,
+ ) = &else_body.kind
+ {
+ if are_exprs_equal(cx, else_expr, body) {
+ if is_testing_negative(cx, cond, body) {
+ span_lint_and_sugg(
+ cx,
+ SUBOPTIMAL_FLOPS,
+ expr.span,
+ "This looks like you've implemented your own absolute value function",
+ "try",
+ format!("{}.abs()", Sugg::hir(cx, body, "..")),
+ Applicability::MachineApplicable,
+ );
+ } else if is_testing_positive(cx, cond, body) {
+ span_lint_and_sugg(
+ cx,
+ SUBOPTIMAL_FLOPS,
+ expr.span,
+ "This looks like you've implemented your own negative absolute value function",
+ "try",
+ format!("-{}.abs()", Sugg::hir(cx, body, "..")),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }