+fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool {
+ if_chain! {
+ if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, args_a, _) = expr_a.kind;
+ if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, args_b, _) = expr_b.kind;
+ then {
+ return method_name_a.as_str() == method_name_b.as_str() &&
+ args_a.len() == args_b.len() &&
+ (
+ ["ln", "log2", "log10"].contains(&method_name_a.as_str()) ||
+ method_name_a.as_str() == "log" && args_a.len() == 2 && eq_expr_value(cx, &args_a[1], &args_b[1])
+ );
+ }
+ }
+
+ false
+}
+
+fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) {
+ // check if expression of the form x.logN() / y.logN()
+ if_chain! {
+ if let ExprKind::Binary(
+ Spanned {
+ node: BinOpKind::Div, ..
+ },
+ lhs,
+ rhs,
+ ) = &expr.kind;
+ if are_same_base_logs(cx, lhs, rhs);
+ if let ExprKind::MethodCall(_, [largs_self, ..], _) = &lhs.kind;
+ if let ExprKind::MethodCall(_, [rargs_self, ..], _) = &rhs.kind;
+ then {
+ span_lint_and_sugg(
+ cx,
+ SUBOPTIMAL_FLOPS,
+ expr.span,
+ "log base can be expressed more clearly",
+ "consider using",
+ format!("{}.log({})", Sugg::hir(cx, largs_self, ".."), Sugg::hir(cx, rargs_self, ".."),),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+}
+
+fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
+ if_chain! {
+ if let ExprKind::Binary(
+ Spanned {
+ node: BinOpKind::Div, ..
+ },
+ div_lhs,
+ div_rhs,
+ ) = &expr.kind;
+ if let ExprKind::Binary(
+ Spanned {
+ node: BinOpKind::Mul, ..
+ },
+ mul_lhs,
+ mul_rhs,
+ ) = &div_lhs.kind;
+ if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), div_rhs);
+ if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), mul_rhs);
+ then {
+ // TODO: also check for constant values near PI/180 or 180/PI
+ if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) &&
+ (F32(180_f32) == lvalue || F64(180_f64) == lvalue)
+ {
+ let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
+ if_chain! {
+ if let ExprKind::Lit(ref literal) = mul_lhs.kind;
+ if let ast::LitKind::Float(ref value, float_type) = literal.node;
+ if float_type == ast::LitFloatType::Unsuffixed;
+ then {
+ if value.as_str().ends_with('.') {
+ proposal = format!("{}0_f64.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
+ } else {
+ proposal = format!("{}_f64.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
+ }
+ }
+ }
+ span_lint_and_sugg(
+ cx,
+ SUBOPTIMAL_FLOPS,
+ expr.span,
+ "conversion to degrees can be done more accurately",
+ "consider using",
+ proposal,
+ Applicability::MachineApplicable,
+ );
+ } else if
+ (F32(180_f32) == rvalue || F64(180_f64) == rvalue) &&
+ (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
+ {
+ let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
+ if_chain! {
+ if let ExprKind::Lit(ref literal) = mul_lhs.kind;
+ if let ast::LitKind::Float(ref value, float_type) = literal.node;
+ if float_type == ast::LitFloatType::Unsuffixed;
+ then {
+ if value.as_str().ends_with('.') {
+ proposal = format!("{}0_f64.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
+ } else {
+ proposal = format!("{}_f64.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
+ }
+ }
+ }
+ span_lint_and_sugg(
+ cx,
+ SUBOPTIMAL_FLOPS,
+ expr.span,
+ "conversion to radians can be done more accurately",
+ "consider using",
+ proposal,
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }
+}
+
+impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ // All of these operations are currently not const.
+ if in_constant(cx, expr.hir_id) {
+ return;
+ }
+
+ if let ExprKind::MethodCall(path, args, _) = &expr.kind {
+ let recv_ty = cx.typeck_results().expr_ty(&args[0]);